| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  net/dccp/ipv4.c | 
|  | 3 | * | 
|  | 4 | *  An implementation of the DCCP protocol | 
|  | 5 | *  Arnaldo Carvalho de Melo <acme@conectiva.com.br> | 
|  | 6 | * | 
|  | 7 | *	This program is free software; you can redistribute it and/or | 
|  | 8 | *	modify it under the terms of the GNU General Public License | 
|  | 9 | *	as published by the Free Software Foundation; either version | 
|  | 10 | *	2 of the License, or (at your option) any later version. | 
|  | 11 | */ | 
|  | 12 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 13 | #include <linux/dccp.h> | 
|  | 14 | #include <linux/icmp.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 15 | #include <linux/slab.h> | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/skbuff.h> | 
|  | 18 | #include <linux/random.h> | 
|  | 19 |  | 
|  | 20 | #include <net/icmp.h> | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 21 | #include <net/inet_common.h> | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 22 | #include <net/inet_hashtables.h> | 
| Arnaldo Carvalho de Melo | 14c8502 | 2005-12-27 02:43:12 -0200 | [diff] [blame] | 23 | #include <net/inet_sock.h> | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 24 | #include <net/protocol.h> | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 25 | #include <net/sock.h> | 
| Arnaldo Carvalho de Melo | 6d6ee43 | 2005-12-13 23:25:19 -0800 | [diff] [blame] | 26 | #include <net/timewait_sock.h> | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 27 | #include <net/tcp_states.h> | 
|  | 28 | #include <net/xfrm.h> | 
| David S. Miller | 6e5714e | 2011-08-03 20:50:44 -0700 | [diff] [blame] | 29 | #include <net/secure_seq.h> | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 30 |  | 
| Arnaldo Carvalho de Melo | ae31c33 | 2005-09-18 00:17:51 -0700 | [diff] [blame] | 31 | #include "ackvec.h" | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 32 | #include "ccid.h" | 
|  | 33 | #include "dccp.h" | 
| Andrea Bittau | afe0025 | 2006-03-20 17:43:56 -0800 | [diff] [blame] | 34 | #include "feat.h" | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 35 |  | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 36 | /* | 
| Pavel Emelyanov | 13f51d8 | 2008-04-14 02:38:45 -0700 | [diff] [blame] | 37 | * The per-net dccp.v4_ctl_sk socket is used for responding to | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 38 | * the Out-of-the-blue (OOTB) packets. A control sock will be created | 
|  | 39 | * for this socket at the initialization time. | 
|  | 40 | */ | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 41 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 42 | int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 43 | { | 
| David S. Miller | 2d7192d | 2011-04-26 13:28:44 -0700 | [diff] [blame] | 44 | const struct sockaddr_in *usin = (struct sockaddr_in *)uaddr; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 45 | struct inet_sock *inet = inet_sk(sk); | 
|  | 46 | struct dccp_sock *dp = dccp_sk(sk); | 
| David S. Miller | dca8b08 | 2011-02-24 13:38:12 -0800 | [diff] [blame] | 47 | __be16 orig_sport, orig_dport; | 
| Al Viro | bada8ad | 2006-09-26 21:27:15 -0700 | [diff] [blame] | 48 | __be32 daddr, nexthop; | 
| David S. Miller | 2c42758 | 2011-05-06 16:10:41 -0700 | [diff] [blame] | 49 | struct flowi4 *fl4; | 
| David S. Miller | 2d7192d | 2011-04-26 13:28:44 -0700 | [diff] [blame] | 50 | struct rtable *rt; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 51 | int err; | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 52 | struct ip_options_rcu *inet_opt; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 53 |  | 
|  | 54 | dp->dccps_role = DCCP_ROLE_CLIENT; | 
|  | 55 |  | 
|  | 56 | if (addr_len < sizeof(struct sockaddr_in)) | 
|  | 57 | return -EINVAL; | 
|  | 58 |  | 
|  | 59 | if (usin->sin_family != AF_INET) | 
|  | 60 | return -EAFNOSUPPORT; | 
|  | 61 |  | 
|  | 62 | nexthop = daddr = usin->sin_addr.s_addr; | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 63 |  | 
|  | 64 | inet_opt = rcu_dereference_protected(inet->inet_opt, | 
|  | 65 | sock_owned_by_user(sk)); | 
|  | 66 | if (inet_opt != NULL && inet_opt->opt.srr) { | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 67 | if (daddr == 0) | 
|  | 68 | return -EINVAL; | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 69 | nexthop = inet_opt->opt.faddr; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 70 | } | 
|  | 71 |  | 
| David S. Miller | dca8b08 | 2011-02-24 13:38:12 -0800 | [diff] [blame] | 72 | orig_sport = inet->inet_sport; | 
|  | 73 | orig_dport = usin->sin_port; | 
| David S. Miller | 2c42758 | 2011-05-06 16:10:41 -0700 | [diff] [blame] | 74 | fl4 = &inet->cork.fl.u.ip4; | 
|  | 75 | rt = ip_route_connect(fl4, nexthop, inet->inet_saddr, | 
| David S. Miller | b23dd4f | 2011-03-02 14:31:35 -0800 | [diff] [blame] | 76 | RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, | 
|  | 77 | IPPROTO_DCCP, | 
|  | 78 | orig_sport, orig_dport, sk, true); | 
|  | 79 | if (IS_ERR(rt)) | 
|  | 80 | return PTR_ERR(rt); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 81 |  | 
|  | 82 | if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { | 
|  | 83 | ip_rt_put(rt); | 
|  | 84 | return -ENETUNREACH; | 
|  | 85 | } | 
|  | 86 |  | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 87 | if (inet_opt == NULL || !inet_opt->opt.srr) | 
| David S. Miller | 2c42758 | 2011-05-06 16:10:41 -0700 | [diff] [blame] | 88 | daddr = fl4->daddr; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 89 |  | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 90 | if (inet->inet_saddr == 0) | 
| David S. Miller | 2c42758 | 2011-05-06 16:10:41 -0700 | [diff] [blame] | 91 | inet->inet_saddr = fl4->saddr; | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 92 | inet->inet_rcv_saddr = inet->inet_saddr; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 93 |  | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 94 | inet->inet_dport = usin->sin_port; | 
|  | 95 | inet->inet_daddr = daddr; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 96 |  | 
| Arnaldo Carvalho de Melo | d83d846 | 2005-12-13 23:26:10 -0800 | [diff] [blame] | 97 | inet_csk(sk)->icsk_ext_hdr_len = 0; | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 98 | if (inet_opt) | 
|  | 99 | inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 100 | /* | 
|  | 101 | * Socket identity is still unknown (sport may be zero). | 
|  | 102 | * However we set state to DCCP_REQUESTING and not releasing socket | 
|  | 103 | * lock select source port, enter ourselves into the hash tables and | 
|  | 104 | * complete initialization after this. | 
|  | 105 | */ | 
|  | 106 | dccp_set_state(sk, DCCP_REQUESTING); | 
| Arnaldo Carvalho de Melo | a7f5e7f | 2005-12-13 23:25:31 -0800 | [diff] [blame] | 107 | err = inet_hash_connect(&dccp_death_row, sk); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 108 | if (err != 0) | 
|  | 109 | goto failure; | 
|  | 110 |  | 
| David S. Miller | 2c42758 | 2011-05-06 16:10:41 -0700 | [diff] [blame] | 111 | rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, | 
| David S. Miller | b23dd4f | 2011-03-02 14:31:35 -0800 | [diff] [blame] | 112 | inet->inet_sport, inet->inet_dport, sk); | 
|  | 113 | if (IS_ERR(rt)) { | 
| RongQing.Li | 525c646 | 2011-11-21 16:45:26 -0500 | [diff] [blame] | 114 | err = PTR_ERR(rt); | 
| David S. Miller | b23dd4f | 2011-03-02 14:31:35 -0800 | [diff] [blame] | 115 | rt = NULL; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 116 | goto failure; | 
| David S. Miller | b23dd4f | 2011-03-02 14:31:35 -0800 | [diff] [blame] | 117 | } | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 118 | /* OK, now commit destination to socket.  */ | 
| Changli Gao | d8d1f30 | 2010-06-10 23:31:35 -0700 | [diff] [blame] | 119 | sk_setup_caps(sk, &rt->dst); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 120 |  | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 121 | dp->dccps_iss = secure_dccp_sequence_number(inet->inet_saddr, | 
|  | 122 | inet->inet_daddr, | 
|  | 123 | inet->inet_sport, | 
|  | 124 | inet->inet_dport); | 
|  | 125 | inet->inet_id = dp->dccps_iss ^ jiffies; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 126 |  | 
|  | 127 | err = dccp_connect(sk); | 
|  | 128 | rt = NULL; | 
|  | 129 | if (err != 0) | 
|  | 130 | goto failure; | 
|  | 131 | out: | 
|  | 132 | return err; | 
|  | 133 | failure: | 
| Arnaldo Carvalho de Melo | 7690af3 | 2005-08-13 20:34:54 -0300 | [diff] [blame] | 134 | /* | 
|  | 135 | * This unhashes the socket and releases the local port, if necessary. | 
|  | 136 | */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 137 | dccp_set_state(sk, DCCP_CLOSED); | 
|  | 138 | ip_rt_put(rt); | 
|  | 139 | sk->sk_route_caps = 0; | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 140 | inet->inet_dport = 0; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 141 | goto out; | 
|  | 142 | } | 
|  | 143 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 144 | EXPORT_SYMBOL_GPL(dccp_v4_connect); | 
|  | 145 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 146 | /* | 
|  | 147 | * This routine does path mtu discovery as defined in RFC1191. | 
|  | 148 | */ | 
|  | 149 | static inline void dccp_do_pmtu_discovery(struct sock *sk, | 
|  | 150 | const struct iphdr *iph, | 
|  | 151 | u32 mtu) | 
|  | 152 | { | 
|  | 153 | struct dst_entry *dst; | 
|  | 154 | const struct inet_sock *inet = inet_sk(sk); | 
|  | 155 | const struct dccp_sock *dp = dccp_sk(sk); | 
|  | 156 |  | 
|  | 157 | /* We are not interested in DCCP_LISTEN and request_socks (RESPONSEs | 
|  | 158 | * send out by Linux are always < 576bytes so they should go through | 
|  | 159 | * unfragmented). | 
|  | 160 | */ | 
|  | 161 | if (sk->sk_state == DCCP_LISTEN) | 
|  | 162 | return; | 
|  | 163 |  | 
|  | 164 | /* We don't check in the destentry if pmtu discovery is forbidden | 
|  | 165 | * on this route. We just assume that no packet_to_big packets | 
|  | 166 | * are send back when pmtu discovery is not active. | 
| YOSHIFUJI Hideaki | c9eaf17 | 2007-02-09 23:24:38 +0900 | [diff] [blame] | 167 | * There is a small race when the user changes this flag in the | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 168 | * route, but I think that's acceptable. | 
|  | 169 | */ | 
|  | 170 | if ((dst = __sk_dst_check(sk, 0)) == NULL) | 
|  | 171 | return; | 
|  | 172 |  | 
|  | 173 | dst->ops->update_pmtu(dst, mtu); | 
|  | 174 |  | 
|  | 175 | /* Something is about to be wrong... Remember soft error | 
|  | 176 | * for the case, if this connection will not able to recover. | 
|  | 177 | */ | 
|  | 178 | if (mtu < dst_mtu(dst) && ip_dont_fragment(sk, dst)) | 
|  | 179 | sk->sk_err_soft = EMSGSIZE; | 
|  | 180 |  | 
|  | 181 | mtu = dst_mtu(dst); | 
|  | 182 |  | 
|  | 183 | if (inet->pmtudisc != IP_PMTUDISC_DONT && | 
| Arnaldo Carvalho de Melo | d83d846 | 2005-12-13 23:26:10 -0800 | [diff] [blame] | 184 | inet_csk(sk)->icsk_pmtu_cookie > mtu) { | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 185 | dccp_sync_mss(sk, mtu); | 
|  | 186 |  | 
|  | 187 | /* | 
| Gerrit Renker | 0e64e94 | 2006-10-24 16:17:51 -0700 | [diff] [blame] | 188 | * From RFC 4340, sec. 14.1: | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 189 | * | 
| Arnaldo Carvalho de Melo | 7690af3 | 2005-08-13 20:34:54 -0300 | [diff] [blame] | 190 | *	DCCP-Sync packets are the best choice for upward | 
|  | 191 | *	probing, since DCCP-Sync probes do not risk application | 
|  | 192 | *	data loss. | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 193 | */ | 
| Arnaldo Carvalho de Melo | e92ae93 | 2005-08-17 03:10:59 -0300 | [diff] [blame] | 194 | dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 195 | } /* else let the usual retransmit timer handle it */ | 
|  | 196 | } | 
|  | 197 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 198 | /* | 
|  | 199 | * This routine is called by the ICMP module when it gets some sort of error | 
|  | 200 | * condition. If err < 0 then the socket should be closed and the error | 
|  | 201 | * returned to the user. If err > 0 it's just the icmp type << 8 | icmp code. | 
|  | 202 | * After adjustment header points to the first 8 bytes of the tcp header. We | 
|  | 203 | * need to find the appropriate port. | 
|  | 204 | * | 
|  | 205 | * The locking strategy used here is very "optimistic". When someone else | 
|  | 206 | * accesses the socket the ICMP is just dropped and for some paths there is no | 
|  | 207 | * check at all. A more general error queue to queue errors for later handling | 
|  | 208 | * is probably better. | 
|  | 209 | */ | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 210 | static void dccp_v4_err(struct sk_buff *skb, u32 info) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 211 | { | 
|  | 212 | const struct iphdr *iph = (struct iphdr *)skb->data; | 
| Wei Yongjun | 18e1d83 | 2008-07-26 11:59:10 +0100 | [diff] [blame] | 213 | const u8 offset = iph->ihl << 2; | 
|  | 214 | const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 215 | struct dccp_sock *dp; | 
|  | 216 | struct inet_sock *inet; | 
| Arnaldo Carvalho de Melo | 88c7664 | 2007-03-13 14:43:18 -0300 | [diff] [blame] | 217 | const int type = icmp_hdr(skb)->type; | 
|  | 218 | const int code = icmp_hdr(skb)->code; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 219 | struct sock *sk; | 
|  | 220 | __u64 seq; | 
|  | 221 | int err; | 
| Pavel Emelyanov | fd54d71 | 2008-07-14 23:01:40 -0700 | [diff] [blame] | 222 | struct net *net = dev_net(skb->dev); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 223 |  | 
| Wei Yongjun | 18e1d83 | 2008-07-26 11:59:10 +0100 | [diff] [blame] | 224 | if (skb->len < offset + sizeof(*dh) || | 
|  | 225 | skb->len < offset + __dccp_basic_hdr_len(dh)) { | 
| Pavel Emelyanov | dcfc23c | 2008-07-14 23:03:00 -0700 | [diff] [blame] | 226 | ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 227 | return; | 
|  | 228 | } | 
|  | 229 |  | 
| Pavel Emelyanov | fd54d71 | 2008-07-14 23:01:40 -0700 | [diff] [blame] | 230 | sk = inet_lookup(net, &dccp_hashinfo, | 
| Pavel Emelyanov | b9901a8 | 2008-04-13 22:30:43 -0700 | [diff] [blame] | 231 | iph->daddr, dh->dccph_dport, | 
|  | 232 | iph->saddr, dh->dccph_sport, inet_iif(skb)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 233 | if (sk == NULL) { | 
| Pavel Emelyanov | dcfc23c | 2008-07-14 23:03:00 -0700 | [diff] [blame] | 234 | ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 235 | return; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | if (sk->sk_state == DCCP_TIME_WAIT) { | 
| YOSHIFUJI Hideaki | 9469c7b | 2006-10-10 19:41:46 -0700 | [diff] [blame] | 239 | inet_twsk_put(inet_twsk(sk)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 240 | return; | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | bh_lock_sock(sk); | 
|  | 244 | /* If too many ICMPs get dropped on busy | 
|  | 245 | * servers this needs to be solved differently. | 
|  | 246 | */ | 
|  | 247 | if (sock_owned_by_user(sk)) | 
| Pavel Emelyanov | de0744a | 2008-07-16 20:31:16 -0700 | [diff] [blame] | 248 | NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 249 |  | 
|  | 250 | if (sk->sk_state == DCCP_CLOSED) | 
|  | 251 | goto out; | 
|  | 252 |  | 
|  | 253 | dp = dccp_sk(sk); | 
| Gerrit Renker | fde2010 | 2007-10-24 10:12:09 -0200 | [diff] [blame] | 254 | seq = dccp_hdr_seq(dh); | 
| Gerrit Renker | 1238d08 | 2007-10-24 10:18:06 -0200 | [diff] [blame] | 255 | if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) && | 
| Wei Yongjun | d68f086 | 2008-07-26 11:59:10 +0100 | [diff] [blame] | 256 | !between48(seq, dp->dccps_awl, dp->dccps_awh)) { | 
| Pavel Emelyanov | de0744a | 2008-07-16 20:31:16 -0700 | [diff] [blame] | 257 | NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 258 | goto out; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | switch (type) { | 
|  | 262 | case ICMP_SOURCE_QUENCH: | 
|  | 263 | /* Just silently ignore these. */ | 
|  | 264 | goto out; | 
|  | 265 | case ICMP_PARAMETERPROB: | 
|  | 266 | err = EPROTO; | 
|  | 267 | break; | 
|  | 268 | case ICMP_DEST_UNREACH: | 
|  | 269 | if (code > NR_ICMP_UNREACH) | 
|  | 270 | goto out; | 
|  | 271 |  | 
|  | 272 | if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */ | 
|  | 273 | if (!sock_owned_by_user(sk)) | 
|  | 274 | dccp_do_pmtu_discovery(sk, iph, info); | 
|  | 275 | goto out; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | err = icmp_err_convert[code].errno; | 
|  | 279 | break; | 
|  | 280 | case ICMP_TIME_EXCEEDED: | 
|  | 281 | err = EHOSTUNREACH; | 
|  | 282 | break; | 
|  | 283 | default: | 
|  | 284 | goto out; | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | switch (sk->sk_state) { | 
|  | 288 | struct request_sock *req , **prev; | 
|  | 289 | case DCCP_LISTEN: | 
|  | 290 | if (sock_owned_by_user(sk)) | 
|  | 291 | goto out; | 
|  | 292 | req = inet_csk_search_req(sk, &prev, dh->dccph_dport, | 
|  | 293 | iph->daddr, iph->saddr); | 
|  | 294 | if (!req) | 
|  | 295 | goto out; | 
|  | 296 |  | 
|  | 297 | /* | 
|  | 298 | * ICMPs are not backlogged, hence we cannot get an established | 
|  | 299 | * socket here. | 
|  | 300 | */ | 
| Ilpo Järvinen | 547b792 | 2008-07-25 21:43:18 -0700 | [diff] [blame] | 301 | WARN_ON(req->sk); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 302 |  | 
| Samuel Jero | f541fb7 | 2012-02-26 18:22:02 -0700 | [diff] [blame] | 303 | if (!between48(seq, dccp_rsk(req)->dreq_iss, | 
|  | 304 | dccp_rsk(req)->dreq_gss)) { | 
| Pavel Emelyanov | de0744a | 2008-07-16 20:31:16 -0700 | [diff] [blame] | 305 | NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 306 | goto out; | 
|  | 307 | } | 
|  | 308 | /* | 
|  | 309 | * Still in RESPOND, just remove it silently. | 
|  | 310 | * There is no good way to pass the error to the newly | 
|  | 311 | * created socket, and POSIX does not want network | 
|  | 312 | * errors returned from accept(). | 
|  | 313 | */ | 
|  | 314 | inet_csk_reqsk_queue_drop(sk, req, prev); | 
|  | 315 | goto out; | 
|  | 316 |  | 
|  | 317 | case DCCP_REQUESTING: | 
|  | 318 | case DCCP_RESPOND: | 
|  | 319 | if (!sock_owned_by_user(sk)) { | 
|  | 320 | DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS); | 
|  | 321 | sk->sk_err = err; | 
|  | 322 |  | 
|  | 323 | sk->sk_error_report(sk); | 
|  | 324 |  | 
|  | 325 | dccp_done(sk); | 
|  | 326 | } else | 
|  | 327 | sk->sk_err_soft = err; | 
|  | 328 | goto out; | 
|  | 329 | } | 
|  | 330 |  | 
|  | 331 | /* If we've already connected we will keep trying | 
|  | 332 | * until we time out, or the user gives up. | 
|  | 333 | * | 
|  | 334 | * rfc1122 4.2.3.9 allows to consider as hard errors | 
|  | 335 | * only PROTO_UNREACH and PORT_UNREACH (well, FRAG_FAILED too, | 
|  | 336 | * but it is obsoleted by pmtu discovery). | 
|  | 337 | * | 
|  | 338 | * Note, that in modern internet, where routing is unreliable | 
|  | 339 | * and in each dark corner broken firewalls sit, sending random | 
|  | 340 | * errors ordered by their masters even this two messages finally lose | 
|  | 341 | * their original sense (even Linux sends invalid PORT_UNREACHs) | 
|  | 342 | * | 
|  | 343 | * Now we are in compliance with RFCs. | 
|  | 344 | *							--ANK (980905) | 
|  | 345 | */ | 
|  | 346 |  | 
|  | 347 | inet = inet_sk(sk); | 
|  | 348 | if (!sock_owned_by_user(sk) && inet->recverr) { | 
|  | 349 | sk->sk_err = err; | 
|  | 350 | sk->sk_error_report(sk); | 
|  | 351 | } else /* Only an error on timeout */ | 
|  | 352 | sk->sk_err_soft = err; | 
|  | 353 | out: | 
|  | 354 | bh_unlock_sock(sk); | 
|  | 355 | sock_put(sk); | 
|  | 356 | } | 
|  | 357 |  | 
| Al Viro | 2bda285 | 2006-11-14 21:28:51 -0800 | [diff] [blame] | 358 | static inline __sum16 dccp_v4_csum_finish(struct sk_buff *skb, | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 359 | __be32 src, __be32 dst) | 
|  | 360 | { | 
|  | 361 | return csum_tcpudp_magic(src, dst, skb->len, IPPROTO_DCCP, skb->csum); | 
|  | 362 | } | 
|  | 363 |  | 
| Herbert Xu | bb29624 | 2010-04-11 02:15:55 +0000 | [diff] [blame] | 364 | void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb) | 
| Arnaldo Carvalho de Melo | 57cca05 | 2005-12-13 23:16:16 -0800 | [diff] [blame] | 365 | { | 
|  | 366 | const struct inet_sock *inet = inet_sk(sk); | 
|  | 367 | struct dccp_hdr *dh = dccp_hdr(skb); | 
|  | 368 |  | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 369 | dccp_csum_outgoing(skb); | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 370 | dh->dccph_checksum = dccp_v4_csum_finish(skb, | 
|  | 371 | inet->inet_saddr, | 
|  | 372 | inet->inet_daddr); | 
| Arnaldo Carvalho de Melo | 57cca05 | 2005-12-13 23:16:16 -0800 | [diff] [blame] | 373 | } | 
|  | 374 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 375 | EXPORT_SYMBOL_GPL(dccp_v4_send_check); | 
|  | 376 |  | 
| Gerrit Renker | 865e902 | 2006-11-13 13:31:50 -0200 | [diff] [blame] | 377 | static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 378 | { | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 379 | return secure_dccp_sequence_number(ip_hdr(skb)->daddr, | 
|  | 380 | ip_hdr(skb)->saddr, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 381 | dccp_hdr(skb)->dccph_dport, | 
|  | 382 | dccp_hdr(skb)->dccph_sport); | 
|  | 383 | } | 
|  | 384 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 385 | /* | 
|  | 386 | * The three way handshake has completed - we got a valid ACK or DATAACK - | 
|  | 387 | * now create the new socket. | 
|  | 388 | * | 
|  | 389 | * This is the equivalent of TCP's tcp_v4_syn_recv_sock | 
|  | 390 | */ | 
|  | 391 | struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, | 
|  | 392 | struct request_sock *req, | 
|  | 393 | struct dst_entry *dst) | 
|  | 394 | { | 
|  | 395 | struct inet_request_sock *ireq; | 
|  | 396 | struct inet_sock *newinet; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 397 | struct sock *newsk; | 
|  | 398 |  | 
|  | 399 | if (sk_acceptq_is_full(sk)) | 
|  | 400 | goto exit_overflow; | 
|  | 401 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 402 | newsk = dccp_create_openreq_child(sk, req, skb); | 
|  | 403 | if (newsk == NULL) | 
| Balazs Scheidler | 093d282 | 2010-10-21 13:06:43 +0200 | [diff] [blame] | 404 | goto exit_nonewsk; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 405 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 406 | newinet		   = inet_sk(newsk); | 
|  | 407 | ireq		   = inet_rsk(req); | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 408 | newinet->inet_daddr	= ireq->rmt_addr; | 
|  | 409 | newinet->inet_rcv_saddr = ireq->loc_addr; | 
|  | 410 | newinet->inet_saddr	= ireq->loc_addr; | 
| Eric Dumazet | f6d8bd0 | 2011-04-21 09:45:37 +0000 | [diff] [blame] | 411 | newinet->inet_opt	= ireq->opt; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 412 | ireq->opt	   = NULL; | 
|  | 413 | newinet->mc_index  = inet_iif(skb); | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 414 | newinet->mc_ttl	   = ip_hdr(skb)->ttl; | 
| Eric Dumazet | c720c7e | 2009-10-15 06:30:45 +0000 | [diff] [blame] | 415 | newinet->inet_id   = jiffies; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 416 |  | 
| David S. Miller | 0e73441 | 2011-05-08 15:28:03 -0700 | [diff] [blame] | 417 | if (dst == NULL && (dst = inet_csk_route_child_sock(sk, newsk, req)) == NULL) | 
|  | 418 | goto put_and_exit; | 
|  | 419 |  | 
|  | 420 | sk_setup_caps(newsk, dst); | 
|  | 421 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 422 | dccp_sync_mss(newsk, dst_mtu(dst)); | 
|  | 423 |  | 
| David S. Miller | 0e73441 | 2011-05-08 15:28:03 -0700 | [diff] [blame] | 424 | if (__inet_inherit_port(sk, newsk) < 0) | 
|  | 425 | goto put_and_exit; | 
| Eric Dumazet | 9327f70 | 2009-12-04 03:46:54 +0000 | [diff] [blame] | 426 | __inet_hash_nolisten(newsk, NULL); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 427 |  | 
|  | 428 | return newsk; | 
|  | 429 |  | 
|  | 430 | exit_overflow: | 
| Pavel Emelyanov | de0744a | 2008-07-16 20:31:16 -0700 | [diff] [blame] | 431 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); | 
| Balazs Scheidler | 093d282 | 2010-10-21 13:06:43 +0200 | [diff] [blame] | 432 | exit_nonewsk: | 
|  | 433 | dst_release(dst); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 434 | exit: | 
| Pavel Emelyanov | de0744a | 2008-07-16 20:31:16 -0700 | [diff] [blame] | 435 | NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 436 | return NULL; | 
| David S. Miller | 0e73441 | 2011-05-08 15:28:03 -0700 | [diff] [blame] | 437 | put_and_exit: | 
| Eric Dumazet | 918eb39 | 2011-11-02 12:42:56 +0000 | [diff] [blame] | 438 | bh_unlock_sock(newsk); | 
| David S. Miller | 0e73441 | 2011-05-08 15:28:03 -0700 | [diff] [blame] | 439 | sock_put(newsk); | 
|  | 440 | goto exit; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 441 | } | 
|  | 442 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 443 | EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock); | 
|  | 444 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 445 | static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) | 
|  | 446 | { | 
|  | 447 | const struct dccp_hdr *dh = dccp_hdr(skb); | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 448 | const struct iphdr *iph = ip_hdr(skb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 449 | struct sock *nsk; | 
|  | 450 | struct request_sock **prev; | 
|  | 451 | /* Find possible connection requests. */ | 
|  | 452 | struct request_sock *req = inet_csk_search_req(sk, &prev, | 
|  | 453 | dh->dccph_sport, | 
|  | 454 | iph->saddr, iph->daddr); | 
|  | 455 | if (req != NULL) | 
|  | 456 | return dccp_check_req(sk, skb, req, prev); | 
|  | 457 |  | 
| Pavel Emelyanov | b9901a8 | 2008-04-13 22:30:43 -0700 | [diff] [blame] | 458 | nsk = inet_lookup_established(sock_net(sk), &dccp_hashinfo, | 
| Herbert Xu | 8f49106 | 2006-08-09 15:47:12 -0700 | [diff] [blame] | 459 | iph->saddr, dh->dccph_sport, | 
|  | 460 | iph->daddr, dh->dccph_dport, | 
|  | 461 | inet_iif(skb)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 462 | if (nsk != NULL) { | 
|  | 463 | if (nsk->sk_state != DCCP_TIME_WAIT) { | 
|  | 464 | bh_lock_sock(nsk); | 
|  | 465 | return nsk; | 
|  | 466 | } | 
| YOSHIFUJI Hideaki | 9469c7b | 2006-10-10 19:41:46 -0700 | [diff] [blame] | 467 | inet_twsk_put(inet_twsk(nsk)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 468 | return NULL; | 
|  | 469 | } | 
|  | 470 |  | 
|  | 471 | return sk; | 
|  | 472 | } | 
|  | 473 |  | 
| Pavel Emelyanov | f548739 | 2008-04-13 22:30:19 -0700 | [diff] [blame] | 474 | static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 475 | struct sk_buff *skb) | 
|  | 476 | { | 
|  | 477 | struct rtable *rt; | 
| David S. Miller | 898f735 | 2011-12-01 13:28:34 -0500 | [diff] [blame] | 478 | const struct iphdr *iph = ip_hdr(skb); | 
| David S. Miller | 9d6ec93 | 2011-03-12 01:12:47 -0500 | [diff] [blame] | 479 | struct flowi4 fl4 = { | 
|  | 480 | .flowi4_oif = skb_rtable(skb)->rt_iif, | 
| David S. Miller | 898f735 | 2011-12-01 13:28:34 -0500 | [diff] [blame] | 481 | .daddr = iph->saddr, | 
|  | 482 | .saddr = iph->daddr, | 
| David S. Miller | 9d6ec93 | 2011-03-12 01:12:47 -0500 | [diff] [blame] | 483 | .flowi4_tos = RT_CONN_FLAGS(sk), | 
|  | 484 | .flowi4_proto = sk->sk_protocol, | 
| David S. Miller | 9cce96d | 2011-03-12 03:00:33 -0500 | [diff] [blame] | 485 | .fl4_sport = dccp_hdr(skb)->dccph_dport, | 
|  | 486 | .fl4_dport = dccp_hdr(skb)->dccph_sport, | 
| David S. Miller | 1d28f42 | 2011-03-12 00:29:39 -0500 | [diff] [blame] | 487 | }; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 488 |  | 
| David S. Miller | 9d6ec93 | 2011-03-12 01:12:47 -0500 | [diff] [blame] | 489 | security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); | 
|  | 490 | rt = ip_route_output_flow(net, &fl4, sk); | 
| David S. Miller | b23dd4f | 2011-03-02 14:31:35 -0800 | [diff] [blame] | 491 | if (IS_ERR(rt)) { | 
| Pavel Emelyanov | 7c73a6f | 2008-07-16 20:20:11 -0700 | [diff] [blame] | 492 | IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 493 | return NULL; | 
|  | 494 | } | 
|  | 495 |  | 
| Changli Gao | d8d1f30 | 2010-06-10 23:31:35 -0700 | [diff] [blame] | 496 | return &rt->dst; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 497 | } | 
|  | 498 |  | 
| William Allen Simpson | e6b4d11 | 2009-12-02 18:07:39 +0000 | [diff] [blame] | 499 | static int dccp_v4_send_response(struct sock *sk, struct request_sock *req, | 
|  | 500 | struct request_values *rv_unused) | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 501 | { | 
|  | 502 | int err = -1; | 
|  | 503 | struct sk_buff *skb; | 
| Denis V. Lunev | fd80eb9 | 2008-02-29 11:43:03 -0800 | [diff] [blame] | 504 | struct dst_entry *dst; | 
| David S. Miller | 6bd023f | 2011-05-18 18:32:03 -0400 | [diff] [blame] | 505 | struct flowi4 fl4; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 506 |  | 
| David S. Miller | 6bd023f | 2011-05-18 18:32:03 -0400 | [diff] [blame] | 507 | dst = inet_csk_route_req(sk, &fl4, req); | 
| Denis V. Lunev | fd80eb9 | 2008-02-29 11:43:03 -0800 | [diff] [blame] | 508 | if (dst == NULL) | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 509 | goto out; | 
|  | 510 |  | 
|  | 511 | skb = dccp_make_response(sk, dst, req); | 
|  | 512 | if (skb != NULL) { | 
|  | 513 | const struct inet_request_sock *ireq = inet_rsk(req); | 
|  | 514 | struct dccp_hdr *dh = dccp_hdr(skb); | 
|  | 515 |  | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 516 | dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->loc_addr, | 
|  | 517 | ireq->rmt_addr); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 518 | err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, | 
|  | 519 | ireq->rmt_addr, | 
|  | 520 | ireq->opt); | 
| Gerrit Renker | b9df3cb | 2006-11-14 11:21:36 -0200 | [diff] [blame] | 521 | err = net_xmit_eval(err); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 522 | } | 
|  | 523 |  | 
|  | 524 | out: | 
|  | 525 | dst_release(dst); | 
|  | 526 | return err; | 
|  | 527 | } | 
|  | 528 |  | 
| YOSHIFUJI Hideaki | cfb6eeb | 2006-11-14 19:07:45 -0800 | [diff] [blame] | 529 | static void dccp_v4_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 530 | { | 
|  | 531 | int err; | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 532 | const struct iphdr *rxiph; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 533 | struct sk_buff *skb; | 
|  | 534 | struct dst_entry *dst; | 
| Eric Dumazet | adf3090 | 2009-06-02 05:19:30 +0000 | [diff] [blame] | 535 | struct net *net = dev_net(skb_dst(rxskb)->dev); | 
| Pavel Emelyanov | b76c4b2 | 2008-04-13 22:29:59 -0700 | [diff] [blame] | 536 | struct sock *ctl_sk = net->dccp.v4_ctl_sk; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 537 |  | 
|  | 538 | /* Never send a reset in response to a reset. */ | 
| Gerrit Renker | e356d37 | 2007-09-26 14:35:19 -0300 | [diff] [blame] | 539 | if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 540 | return; | 
|  | 541 |  | 
| Eric Dumazet | 511c3f9 | 2009-06-02 05:14:27 +0000 | [diff] [blame] | 542 | if (skb_rtable(rxskb)->rt_type != RTN_LOCAL) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 543 | return; | 
|  | 544 |  | 
| Pavel Emelyanov | f548739 | 2008-04-13 22:30:19 -0700 | [diff] [blame] | 545 | dst = dccp_v4_route_skb(net, ctl_sk, rxskb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 546 | if (dst == NULL) | 
|  | 547 | return; | 
|  | 548 |  | 
| Pavel Emelyanov | 7b1cffa | 2008-04-13 22:29:37 -0700 | [diff] [blame] | 549 | skb = dccp_ctl_make_reset(ctl_sk, rxskb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 550 | if (skb == NULL) | 
|  | 551 | goto out; | 
|  | 552 |  | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 553 | rxiph = ip_hdr(rxskb); | 
| Gerrit Renker | e356d37 | 2007-09-26 14:35:19 -0300 | [diff] [blame] | 554 | dccp_hdr(skb)->dccph_checksum = dccp_v4_csum_finish(skb, rxiph->saddr, | 
|  | 555 | rxiph->daddr); | 
| Eric Dumazet | adf3090 | 2009-06-02 05:19:30 +0000 | [diff] [blame] | 556 | skb_dst_set(skb, dst_clone(dst)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 557 |  | 
| Pavel Emelyanov | 7b1cffa | 2008-04-13 22:29:37 -0700 | [diff] [blame] | 558 | bh_lock_sock(ctl_sk); | 
|  | 559 | err = ip_build_and_send_pkt(skb, ctl_sk, | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 560 | rxiph->daddr, rxiph->saddr, NULL); | 
| Pavel Emelyanov | 7b1cffa | 2008-04-13 22:29:37 -0700 | [diff] [blame] | 561 | bh_unlock_sock(ctl_sk); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 562 |  | 
| Gerrit Renker | b9df3cb | 2006-11-14 11:21:36 -0200 | [diff] [blame] | 563 | if (net_xmit_eval(err) == 0) { | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 564 | DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); | 
|  | 565 | DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); | 
|  | 566 | } | 
|  | 567 | out: | 
|  | 568 | dst_release(dst); | 
|  | 569 | } | 
|  | 570 |  | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 571 | static void dccp_v4_reqsk_destructor(struct request_sock *req) | 
|  | 572 | { | 
| Gerrit Renker | d99a7bd | 2008-11-04 23:56:30 -0800 | [diff] [blame] | 573 | dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 574 | kfree(inet_rsk(req)->opt); | 
|  | 575 | } | 
|  | 576 |  | 
|  | 577 | static struct request_sock_ops dccp_request_sock_ops __read_mostly = { | 
|  | 578 | .family		= PF_INET, | 
|  | 579 | .obj_size	= sizeof(struct dccp_request_sock), | 
|  | 580 | .rtx_syn_ack	= dccp_v4_send_response, | 
|  | 581 | .send_ack	= dccp_reqsk_send_ack, | 
|  | 582 | .destructor	= dccp_v4_reqsk_destructor, | 
|  | 583 | .send_reset	= dccp_v4_ctl_send_reset, | 
|  | 584 | }; | 
|  | 585 |  | 
|  | 586 | int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) | 
|  | 587 | { | 
|  | 588 | struct inet_request_sock *ireq; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 589 | struct request_sock *req; | 
|  | 590 | struct dccp_request_sock *dreq; | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 591 | const __be32 service = dccp_hdr_request(skb)->dccph_req_service; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 592 | struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 593 |  | 
|  | 594 | /* Never answer to DCCP_PKT_REQUESTs send to broadcast or multicast */ | 
| Eric Dumazet | 511c3f9 | 2009-06-02 05:14:27 +0000 | [diff] [blame] | 595 | if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) | 
| Gerrit Renker | 4a5409a | 2007-10-04 14:52:28 -0700 | [diff] [blame] | 596 | return 0;	/* discard, don't send a reset here */ | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 597 |  | 
|  | 598 | if (dccp_bad_service_code(sk, service)) { | 
| Gerrit Renker | 4a5409a | 2007-10-04 14:52:28 -0700 | [diff] [blame] | 599 | dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 600 | goto drop; | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 601 | } | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 602 | /* | 
|  | 603 | * TW buckets are converted to open requests without | 
|  | 604 | * limitations, they conserve resources and peer is | 
|  | 605 | * evidently real one. | 
|  | 606 | */ | 
| Gerrit Renker | 4a5409a | 2007-10-04 14:52:28 -0700 | [diff] [blame] | 607 | dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 608 | if (inet_csk_reqsk_queue_is_full(sk)) | 
|  | 609 | goto drop; | 
|  | 610 |  | 
|  | 611 | /* | 
|  | 612 | * Accept backlog is full. If we have already queued enough | 
|  | 613 | * of warm entries in syn queue, drop request. It is better than | 
|  | 614 | * clogging syn queue with openreqs with exponentially increasing | 
|  | 615 | * timeout. | 
|  | 616 | */ | 
|  | 617 | if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) | 
|  | 618 | goto drop; | 
|  | 619 |  | 
| Arnaldo Carvalho de Melo | ce4a7d0 | 2008-06-10 12:39:35 -0700 | [diff] [blame] | 620 | req = inet_reqsk_alloc(&dccp_request_sock_ops); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 621 | if (req == NULL) | 
|  | 622 | goto drop; | 
|  | 623 |  | 
| Gerrit Renker | ac75773 | 2008-11-04 23:55:49 -0800 | [diff] [blame] | 624 | if (dccp_reqsk_init(req, dccp_sk(sk), skb)) | 
|  | 625 | goto drop_and_free; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 626 |  | 
| Gerrit Renker | 8b81941 | 2007-12-13 12:29:24 -0200 | [diff] [blame] | 627 | dreq = dccp_rsk(req); | 
|  | 628 | if (dccp_parse_options(sk, dreq, skb)) | 
|  | 629 | goto drop_and_free; | 
|  | 630 |  | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 631 | if (security_inet_conn_request(sk, skb, req)) | 
|  | 632 | goto drop_and_free; | 
|  | 633 |  | 
|  | 634 | ireq = inet_rsk(req); | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 635 | ireq->loc_addr = ip_hdr(skb)->daddr; | 
|  | 636 | ireq->rmt_addr = ip_hdr(skb)->saddr; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 637 |  | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 638 | /* | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 639 | * Step 3: Process LISTEN state | 
|  | 640 | * | 
|  | 641 | * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie | 
|  | 642 | * | 
| Samuel Jero | f541fb7 | 2012-02-26 18:22:02 -0700 | [diff] [blame] | 643 | * Setting S.SWL/S.SWH to is deferred to dccp_create_openreq_child(). | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 644 | */ | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 645 | dreq->dreq_isr	   = dcb->dccpd_seq; | 
| Samuel Jero | f541fb7 | 2012-02-26 18:22:02 -0700 | [diff] [blame] | 646 | dreq->dreq_gsr	   = dreq->dreq_isr; | 
| Gerrit Renker | 865e902 | 2006-11-13 13:31:50 -0200 | [diff] [blame] | 647 | dreq->dreq_iss	   = dccp_v4_init_sequence(skb); | 
| Samuel Jero | f541fb7 | 2012-02-26 18:22:02 -0700 | [diff] [blame] | 648 | dreq->dreq_gss     = dreq->dreq_iss; | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 649 | dreq->dreq_service = service; | 
|  | 650 |  | 
| William Allen Simpson | e6b4d11 | 2009-12-02 18:07:39 +0000 | [diff] [blame] | 651 | if (dccp_v4_send_response(sk, req, NULL)) | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 652 | goto drop_and_free; | 
|  | 653 |  | 
|  | 654 | inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); | 
|  | 655 | return 0; | 
|  | 656 |  | 
|  | 657 | drop_and_free: | 
|  | 658 | reqsk_free(req); | 
|  | 659 | drop: | 
|  | 660 | DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS); | 
| Gerrit Renker | 3d2fe62 | 2006-11-10 12:52:36 -0200 | [diff] [blame] | 661 | return -1; | 
|  | 662 | } | 
|  | 663 |  | 
|  | 664 | EXPORT_SYMBOL_GPL(dccp_v4_conn_request); | 
|  | 665 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 666 | int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) | 
|  | 667 | { | 
|  | 668 | struct dccp_hdr *dh = dccp_hdr(skb); | 
|  | 669 |  | 
|  | 670 | if (sk->sk_state == DCCP_OPEN) { /* Fast path */ | 
|  | 671 | if (dccp_rcv_established(sk, skb, dh, skb->len)) | 
|  | 672 | goto reset; | 
|  | 673 | return 0; | 
|  | 674 | } | 
|  | 675 |  | 
|  | 676 | /* | 
|  | 677 | *  Step 3: Process LISTEN state | 
| Gerrit Renker | d83ca5a | 2006-11-10 16:29:14 -0200 | [diff] [blame] | 678 | *	 If P.type == Request or P contains a valid Init Cookie option, | 
|  | 679 | *	      (* Must scan the packet's options to check for Init | 
|  | 680 | *		 Cookies.  Only Init Cookies are processed here, | 
|  | 681 | *		 however; other options are processed in Step 8.  This | 
|  | 682 | *		 scan need only be performed if the endpoint uses Init | 
|  | 683 | *		 Cookies *) | 
|  | 684 | *	      (* Generate a new socket and switch to that socket *) | 
|  | 685 | *	      Set S := new socket for this port pair | 
|  | 686 | *	      S.state = RESPOND | 
|  | 687 | *	      Choose S.ISS (initial seqno) or set from Init Cookies | 
|  | 688 | *	      Initialize S.GAR := S.ISS | 
|  | 689 | *	      Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies | 
|  | 690 | *	      Continue with S.state == RESPOND | 
|  | 691 | *	      (* A Response packet will be generated in Step 11 *) | 
|  | 692 | *	 Otherwise, | 
|  | 693 | *	      Generate Reset(No Connection) unless P.type == Reset | 
|  | 694 | *	      Drop packet and return | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 695 | * | 
| Arnaldo Carvalho de Melo | 7690af3 | 2005-08-13 20:34:54 -0300 | [diff] [blame] | 696 | * NOTE: the check for the packet types is done in | 
|  | 697 | *	 dccp_rcv_state_process | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 698 | */ | 
|  | 699 | if (sk->sk_state == DCCP_LISTEN) { | 
|  | 700 | struct sock *nsk = dccp_v4_hnd_req(sk, skb); | 
|  | 701 |  | 
|  | 702 | if (nsk == NULL) | 
|  | 703 | goto discard; | 
|  | 704 |  | 
|  | 705 | if (nsk != sk) { | 
|  | 706 | if (dccp_child_process(sk, nsk, skb)) | 
|  | 707 | goto reset; | 
|  | 708 | return 0; | 
|  | 709 | } | 
|  | 710 | } | 
|  | 711 |  | 
|  | 712 | if (dccp_rcv_state_process(sk, skb, dh, skb->len)) | 
|  | 713 | goto reset; | 
|  | 714 | return 0; | 
|  | 715 |  | 
|  | 716 | reset: | 
| YOSHIFUJI Hideaki | cfb6eeb | 2006-11-14 19:07:45 -0800 | [diff] [blame] | 717 | dccp_v4_ctl_send_reset(sk, skb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 718 | discard: | 
|  | 719 | kfree_skb(skb); | 
|  | 720 | return 0; | 
|  | 721 | } | 
|  | 722 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 723 | EXPORT_SYMBOL_GPL(dccp_v4_do_rcv); | 
|  | 724 |  | 
| Gerrit Renker | 09dbc38 | 2006-11-14 12:57:34 -0200 | [diff] [blame] | 725 | /** | 
|  | 726 | *	dccp_invalid_packet  -  check for malformed packets | 
|  | 727 | *	Implements RFC 4340, 8.5:  Step 1: Check header basics | 
|  | 728 | *	Packets that fail these checks are ignored and do not receive Resets. | 
|  | 729 | */ | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 730 | int dccp_invalid_packet(struct sk_buff *skb) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 731 | { | 
|  | 732 | const struct dccp_hdr *dh; | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 733 | unsigned int cscov; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 734 |  | 
|  | 735 | if (skb->pkt_type != PACKET_HOST) | 
|  | 736 | return 1; | 
|  | 737 |  | 
| Gerrit Renker | 09dbc38 | 2006-11-14 12:57:34 -0200 | [diff] [blame] | 738 | /* If the packet is shorter than 12 bytes, drop packet and return */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 739 | if (!pskb_may_pull(skb, sizeof(struct dccp_hdr))) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 740 | DCCP_WARN("pskb_may_pull failed\n"); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 741 | return 1; | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | dh = dccp_hdr(skb); | 
|  | 745 |  | 
| Gerrit Renker | 09dbc38 | 2006-11-14 12:57:34 -0200 | [diff] [blame] | 746 | /* If P.type is not understood, drop packet and return */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 747 | if (dh->dccph_type >= DCCP_PKT_INVALID) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 748 | DCCP_WARN("invalid packet type\n"); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 749 | return 1; | 
|  | 750 | } | 
|  | 751 |  | 
|  | 752 | /* | 
| Gerrit Renker | 09dbc38 | 2006-11-14 12:57:34 -0200 | [diff] [blame] | 753 | * If P.Data Offset is too small for packet type, drop packet and return | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 754 | */ | 
|  | 755 | if (dh->dccph_doff < dccp_hdr_len(skb) / sizeof(u32)) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 756 | DCCP_WARN("P.Data Offset(%u) too small\n", dh->dccph_doff); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 757 | return 1; | 
|  | 758 | } | 
| Gerrit Renker | 09dbc38 | 2006-11-14 12:57:34 -0200 | [diff] [blame] | 759 | /* | 
|  | 760 | * If P.Data Offset is too too large for packet, drop packet and return | 
|  | 761 | */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 762 | if (!pskb_may_pull(skb, dh->dccph_doff * sizeof(u32))) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 763 | DCCP_WARN("P.Data Offset(%u) too large\n", dh->dccph_doff); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 764 | return 1; | 
|  | 765 | } | 
|  | 766 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 767 | /* | 
|  | 768 | * If P.type is not Data, Ack, or DataAck and P.X == 0 (the packet | 
|  | 769 | * has short sequence numbers), drop packet and return | 
|  | 770 | */ | 
| Wei Yongjun | 6079a46 | 2008-05-27 06:22:38 -0700 | [diff] [blame] | 771 | if ((dh->dccph_type < DCCP_PKT_DATA    || | 
|  | 772 | dh->dccph_type > DCCP_PKT_DATAACK) && dh->dccph_x == 0)  { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 773 | DCCP_WARN("P.type (%s) not Data || [Data]Ack, while P.X == 0\n", | 
|  | 774 | dccp_packet_name(dh->dccph_type)); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 775 | return 1; | 
|  | 776 | } | 
|  | 777 |  | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 778 | /* | 
|  | 779 | * If P.CsCov is too large for the packet size, drop packet and return. | 
|  | 780 | * This must come _before_ checksumming (not as RFC 4340 suggests). | 
|  | 781 | */ | 
|  | 782 | cscov = dccp_csum_coverage(skb); | 
|  | 783 | if (cscov > skb->len) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 784 | DCCP_WARN("P.CsCov %u exceeds packet length %d\n", | 
|  | 785 | dh->dccph_cscov, skb->len); | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 786 | return 1; | 
|  | 787 | } | 
|  | 788 |  | 
|  | 789 | /* If header checksum is incorrect, drop packet and return. | 
|  | 790 | * (This step is completed in the AF-dependent functions.) */ | 
|  | 791 | skb->csum = skb_checksum(skb, 0, cscov, 0); | 
|  | 792 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 793 | return 0; | 
|  | 794 | } | 
|  | 795 |  | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 796 | EXPORT_SYMBOL_GPL(dccp_invalid_packet); | 
|  | 797 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 798 | /* this is called when real data arrives */ | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 799 | static int dccp_v4_rcv(struct sk_buff *skb) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 800 | { | 
|  | 801 | const struct dccp_hdr *dh; | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 802 | const struct iphdr *iph; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 803 | struct sock *sk; | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 804 | int min_cov; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 805 |  | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 806 | /* Step 1: Check header basics */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 807 |  | 
|  | 808 | if (dccp_invalid_packet(skb)) | 
|  | 809 | goto discard_it; | 
|  | 810 |  | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 811 | iph = ip_hdr(skb); | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 812 | /* Step 1: If header checksum is incorrect, drop packet and return */ | 
| Arnaldo Carvalho de Melo | eddc9ec | 2007-04-20 22:47:35 -0700 | [diff] [blame] | 813 | if (dccp_v4_csum_finish(skb, iph->saddr, iph->daddr)) { | 
| Gerrit Renker | 59348b1 | 2006-11-20 18:39:23 -0200 | [diff] [blame] | 814 | DCCP_WARN("dropped packet with invalid checksum\n"); | 
| Arnaldo Carvalho de Melo | f21e68c | 2005-12-13 23:24:16 -0800 | [diff] [blame] | 815 | goto discard_it; | 
|  | 816 | } | 
|  | 817 |  | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 818 | dh = dccp_hdr(skb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 819 |  | 
| Gerrit Renker | fde2010 | 2007-10-24 10:12:09 -0200 | [diff] [blame] | 820 | DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(dh); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 821 | DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type; | 
|  | 822 |  | 
| Harvey Harrison | 21454aa | 2008-10-31 00:54:56 -0700 | [diff] [blame] | 823 | dccp_pr_debug("%8.8s src=%pI4@%-5d dst=%pI4@%-5d seq=%llu", | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 824 | dccp_packet_name(dh->dccph_type), | 
| Harvey Harrison | 21454aa | 2008-10-31 00:54:56 -0700 | [diff] [blame] | 825 | &iph->saddr, ntohs(dh->dccph_sport), | 
|  | 826 | &iph->daddr, ntohs(dh->dccph_dport), | 
| David S. Miller | f6ccf55 | 2005-08-09 20:27:14 -0700 | [diff] [blame] | 827 | (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 828 |  | 
|  | 829 | if (dccp_packet_without_ack(skb)) { | 
|  | 830 | DCCP_SKB_CB(skb)->dccpd_ack_seq = DCCP_PKT_WITHOUT_ACK_SEQ; | 
|  | 831 | dccp_pr_debug_cat("\n"); | 
|  | 832 | } else { | 
|  | 833 | DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb); | 
| Gerrit Renker | d23c710 | 2006-11-10 11:46:34 -0200 | [diff] [blame] | 834 | dccp_pr_debug_cat(", ack=%llu\n", (unsigned long long) | 
| David S. Miller | f6ccf55 | 2005-08-09 20:27:14 -0700 | [diff] [blame] | 835 | DCCP_SKB_CB(skb)->dccpd_ack_seq); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 836 | } | 
|  | 837 |  | 
|  | 838 | /* Step 2: | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 839 | *	Look up flow ID in table and get corresponding socket */ | 
| Arnaldo Carvalho de Melo | 9a1f27c | 2008-10-07 11:41:57 -0700 | [diff] [blame] | 840 | sk = __inet_lookup_skb(&dccp_hashinfo, skb, | 
|  | 841 | dh->dccph_sport, dh->dccph_dport); | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 842 | /* | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 843 | * Step 2: | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 844 | *	If no socket ... | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 845 | */ | 
|  | 846 | if (sk == NULL) { | 
|  | 847 | dccp_pr_debug("failed to look up flow ID in table and " | 
|  | 848 | "get corresponding socket\n"); | 
|  | 849 | goto no_dccp_socket; | 
|  | 850 | } | 
|  | 851 |  | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 852 | /* | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 853 | * Step 2: | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 854 | *	... or S.state == TIMEWAIT, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 855 | *		Generate Reset(No Connection) unless P.type == Reset | 
|  | 856 | *		Drop packet and return | 
|  | 857 | */ | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 858 | if (sk->sk_state == DCCP_TIME_WAIT) { | 
| Gerrit Renker | d23c710 | 2006-11-10 11:46:34 -0200 | [diff] [blame] | 859 | dccp_pr_debug("sk->sk_state == DCCP_TIME_WAIT: do_time_wait\n"); | 
|  | 860 | inet_twsk_put(inet_twsk(sk)); | 
|  | 861 | goto no_dccp_socket; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 862 | } | 
|  | 863 |  | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 864 | /* | 
|  | 865 | * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 866 | *	o if MinCsCov = 0, only packets with CsCov = 0 are accepted | 
|  | 867 | *	o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov | 
| Gerrit Renker | 6f4e5ff | 2006-11-10 17:43:06 -0200 | [diff] [blame] | 868 | */ | 
|  | 869 | min_cov = dccp_sk(sk)->dccps_pcrlen; | 
|  | 870 | if (dh->dccph_cscov && (min_cov == 0 || dh->dccph_cscov < min_cov))  { | 
|  | 871 | dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n", | 
|  | 872 | dh->dccph_cscov, min_cov); | 
|  | 873 | /* FIXME: "Such packets SHOULD be reported using Data Dropped | 
|  | 874 | *         options (Section 11.7) with Drop Code 0, Protocol | 
|  | 875 | *         Constraints."                                     */ | 
|  | 876 | goto discard_and_relse; | 
|  | 877 | } | 
|  | 878 |  | 
| Arnaldo Carvalho de Melo | 25995ff | 2005-12-27 02:42:22 -0200 | [diff] [blame] | 879 | if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 880 | goto discard_and_relse; | 
| Patrick McHardy | eb9c7eb | 2006-01-06 23:06:30 -0800 | [diff] [blame] | 881 | nf_reset(skb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 882 |  | 
| Arnaldo Carvalho de Melo | 58a5a7b | 2006-11-16 14:06:06 -0200 | [diff] [blame] | 883 | return sk_receive_skb(sk, skb, 1); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 884 |  | 
|  | 885 | no_dccp_socket: | 
|  | 886 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) | 
|  | 887 | goto discard_it; | 
|  | 888 | /* | 
|  | 889 | * Step 2: | 
| Arnaldo Carvalho de Melo | 8109b02 | 2006-12-10 16:01:18 -0200 | [diff] [blame] | 890 | *	If no socket ... | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 891 | *		Generate Reset(No Connection) unless P.type == Reset | 
|  | 892 | *		Drop packet and return | 
|  | 893 | */ | 
|  | 894 | if (dh->dccph_type != DCCP_PKT_RESET) { | 
| Arnaldo Carvalho de Melo | 7690af3 | 2005-08-13 20:34:54 -0300 | [diff] [blame] | 895 | DCCP_SKB_CB(skb)->dccpd_reset_code = | 
|  | 896 | DCCP_RESET_CODE_NO_CONNECTION; | 
| YOSHIFUJI Hideaki | cfb6eeb | 2006-11-14 19:07:45 -0800 | [diff] [blame] | 897 | dccp_v4_ctl_send_reset(sk, skb); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 898 | } | 
|  | 899 |  | 
|  | 900 | discard_it: | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 901 | kfree_skb(skb); | 
|  | 902 | return 0; | 
|  | 903 |  | 
|  | 904 | discard_and_relse: | 
|  | 905 | sock_put(sk); | 
|  | 906 | goto discard_it; | 
|  | 907 | } | 
|  | 908 |  | 
| Stephen Hemminger | 3b401a8 | 2009-09-01 19:25:04 +0000 | [diff] [blame] | 909 | static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = { | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 910 | .queue_xmit	   = ip_queue_xmit, | 
|  | 911 | .send_check	   = dccp_v4_send_check, | 
|  | 912 | .rebuild_header	   = inet_sk_rebuild_header, | 
|  | 913 | .conn_request	   = dccp_v4_conn_request, | 
|  | 914 | .syn_recv_sock	   = dccp_v4_request_recv_sock, | 
|  | 915 | .net_header_len	   = sizeof(struct iphdr), | 
|  | 916 | .setsockopt	   = ip_setsockopt, | 
|  | 917 | .getsockopt	   = ip_getsockopt, | 
|  | 918 | .addr2sockaddr	   = inet_csk_addr2sockaddr, | 
|  | 919 | .sockaddr_len	   = sizeof(struct sockaddr_in), | 
| Arnaldo Carvalho de Melo | ab1e0a1 | 2008-02-03 04:06:04 -0800 | [diff] [blame] | 920 | .bind_conflict	   = inet_csk_bind_conflict, | 
| Dmitry Mishin | 3fdadf7 | 2006-03-20 22:45:21 -0800 | [diff] [blame] | 921 | #ifdef CONFIG_COMPAT | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 922 | .compat_setsockopt = compat_ip_setsockopt, | 
|  | 923 | .compat_getsockopt = compat_ip_getsockopt, | 
| Dmitry Mishin | 3fdadf7 | 2006-03-20 22:45:21 -0800 | [diff] [blame] | 924 | #endif | 
| Arnaldo Carvalho de Melo | 57cca05 | 2005-12-13 23:16:16 -0800 | [diff] [blame] | 925 | }; | 
|  | 926 |  | 
| Arnaldo Carvalho de Melo | 3e0fadc | 2006-03-20 21:23:15 -0800 | [diff] [blame] | 927 | static int dccp_v4_init_sock(struct sock *sk) | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 928 | { | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 929 | static __u8 dccp_v4_ctl_sock_initialized; | 
|  | 930 | int err = dccp_init_sock(sk, dccp_v4_ctl_sock_initialized); | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 931 |  | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 932 | if (err == 0) { | 
|  | 933 | if (unlikely(!dccp_v4_ctl_sock_initialized)) | 
|  | 934 | dccp_v4_ctl_sock_initialized = 1; | 
| Arnaldo Carvalho de Melo | 3e0fadc | 2006-03-20 21:23:15 -0800 | [diff] [blame] | 935 | inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops; | 
| Arnaldo Carvalho de Melo | 7247887 | 2006-03-20 22:00:37 -0800 | [diff] [blame] | 936 | } | 
|  | 937 |  | 
| Arnaldo Carvalho de Melo | 3e0fadc | 2006-03-20 21:23:15 -0800 | [diff] [blame] | 938 | return err; | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 939 | } | 
|  | 940 |  | 
| Arnaldo Carvalho de Melo | 6d6ee43 | 2005-12-13 23:25:19 -0800 | [diff] [blame] | 941 | static struct timewait_sock_ops dccp_timewait_sock_ops = { | 
|  | 942 | .twsk_obj_size	= sizeof(struct inet_timewait_sock), | 
|  | 943 | }; | 
|  | 944 |  | 
| Adrian Bunk | 5e0817f | 2006-03-20 21:58:29 -0800 | [diff] [blame] | 945 | static struct proto dccp_v4_prot = { | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 946 | .name			= "DCCP", | 
|  | 947 | .owner			= THIS_MODULE, | 
|  | 948 | .close			= dccp_close, | 
|  | 949 | .connect		= dccp_v4_connect, | 
|  | 950 | .disconnect		= dccp_disconnect, | 
|  | 951 | .ioctl			= dccp_ioctl, | 
|  | 952 | .init			= dccp_v4_init_sock, | 
|  | 953 | .setsockopt		= dccp_setsockopt, | 
|  | 954 | .getsockopt		= dccp_getsockopt, | 
|  | 955 | .sendmsg		= dccp_sendmsg, | 
|  | 956 | .recvmsg		= dccp_recvmsg, | 
|  | 957 | .backlog_rcv		= dccp_v4_do_rcv, | 
| Arnaldo Carvalho de Melo | ab1e0a1 | 2008-02-03 04:06:04 -0800 | [diff] [blame] | 958 | .hash			= inet_hash, | 
|  | 959 | .unhash			= inet_unhash, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 960 | .accept			= inet_csk_accept, | 
| Arnaldo Carvalho de Melo | ab1e0a1 | 2008-02-03 04:06:04 -0800 | [diff] [blame] | 961 | .get_port		= inet_csk_get_port, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 962 | .shutdown		= dccp_shutdown, | 
| Arnaldo Carvalho de Melo | 3e0fadc | 2006-03-20 21:23:15 -0800 | [diff] [blame] | 963 | .destroy		= dccp_destroy_sock, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 964 | .orphan_count		= &dccp_orphan_count, | 
|  | 965 | .max_header		= MAX_DCCP_HEADER, | 
|  | 966 | .obj_size		= sizeof(struct dccp_sock), | 
| Eric Dumazet | 3ab5aee | 2008-11-16 19:40:17 -0800 | [diff] [blame] | 967 | .slab_flags		= SLAB_DESTROY_BY_RCU, | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 968 | .rsk_prot		= &dccp_request_sock_ops, | 
| Arnaldo Carvalho de Melo | 6d6ee43 | 2005-12-13 23:25:19 -0800 | [diff] [blame] | 969 | .twsk_prot		= &dccp_timewait_sock_ops, | 
| Pavel Emelyanov | 39d8cda | 2008-03-22 16:50:58 -0700 | [diff] [blame] | 970 | .h.hashinfo		= &dccp_hashinfo, | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 971 | #ifdef CONFIG_COMPAT | 
|  | 972 | .compat_setsockopt	= compat_dccp_setsockopt, | 
|  | 973 | .compat_getsockopt	= compat_dccp_getsockopt, | 
|  | 974 | #endif | 
| Arnaldo Carvalho de Melo | 7c65787 | 2005-08-09 20:14:34 -0700 | [diff] [blame] | 975 | }; | 
| Arnaldo Carvalho de Melo | 6d6ee43 | 2005-12-13 23:25:19 -0800 | [diff] [blame] | 976 |  | 
| Alexey Dobriyan | 3261309 | 2009-09-14 12:21:47 +0000 | [diff] [blame] | 977 | static const struct net_protocol dccp_v4_protocol = { | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 978 | .handler	= dccp_v4_rcv, | 
|  | 979 | .err_handler	= dccp_v4_err, | 
|  | 980 | .no_policy	= 1, | 
| Pavel Emelyanov | fc5f858 | 2008-04-13 22:31:05 -0700 | [diff] [blame] | 981 | .netns_ok	= 1, | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 982 | }; | 
|  | 983 |  | 
|  | 984 | static const struct proto_ops inet_dccp_ops = { | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 985 | .family		   = PF_INET, | 
|  | 986 | .owner		   = THIS_MODULE, | 
|  | 987 | .release	   = inet_release, | 
|  | 988 | .bind		   = inet_bind, | 
|  | 989 | .connect	   = inet_stream_connect, | 
|  | 990 | .socketpair	   = sock_no_socketpair, | 
|  | 991 | .accept		   = inet_accept, | 
|  | 992 | .getname	   = inet_getname, | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 993 | /* FIXME: work on tcp_poll to rename it to inet_csk_poll */ | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 994 | .poll		   = dccp_poll, | 
|  | 995 | .ioctl		   = inet_ioctl, | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 996 | /* FIXME: work on inet_listen to rename it to sock_common_listen */ | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 997 | .listen		   = inet_dccp_listen, | 
|  | 998 | .shutdown	   = inet_shutdown, | 
|  | 999 | .setsockopt	   = sock_common_setsockopt, | 
|  | 1000 | .getsockopt	   = sock_common_getsockopt, | 
|  | 1001 | .sendmsg	   = inet_sendmsg, | 
|  | 1002 | .recvmsg	   = sock_common_recvmsg, | 
|  | 1003 | .mmap		   = sock_no_mmap, | 
|  | 1004 | .sendpage	   = sock_no_sendpage, | 
| Dmitry Mishin | 3fdadf7 | 2006-03-20 22:45:21 -0800 | [diff] [blame] | 1005 | #ifdef CONFIG_COMPAT | 
| Arnaldo Carvalho de Melo | 543d9cf | 2006-03-20 22:48:35 -0800 | [diff] [blame] | 1006 | .compat_setsockopt = compat_sock_common_setsockopt, | 
|  | 1007 | .compat_getsockopt = compat_sock_common_getsockopt, | 
| Dmitry Mishin | 3fdadf7 | 2006-03-20 22:45:21 -0800 | [diff] [blame] | 1008 | #endif | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1009 | }; | 
|  | 1010 |  | 
|  | 1011 | static struct inet_protosw dccp_v4_protosw = { | 
|  | 1012 | .type		= SOCK_DCCP, | 
|  | 1013 | .protocol	= IPPROTO_DCCP, | 
|  | 1014 | .prot		= &dccp_v4_prot, | 
|  | 1015 | .ops		= &inet_dccp_ops, | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1016 | .no_check	= 0, | 
|  | 1017 | .flags		= INET_PROTOSW_ICSK, | 
|  | 1018 | }; | 
|  | 1019 |  | 
| Alexey Dobriyan | 2c8c1e7 | 2010-01-17 03:35:32 +0000 | [diff] [blame] | 1020 | static int __net_init dccp_v4_init_net(struct net *net) | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1021 | { | 
| Gerrit Renker | d14a0eb | 2010-03-14 20:13:19 +0000 | [diff] [blame] | 1022 | if (dccp_hashinfo.bhash == NULL) | 
|  | 1023 | return -ESOCKTNOSUPPORT; | 
| Pavel Emelyanov | b76c4b2 | 2008-04-13 22:29:59 -0700 | [diff] [blame] | 1024 |  | 
| Gerrit Renker | d14a0eb | 2010-03-14 20:13:19 +0000 | [diff] [blame] | 1025 | return inet_ctl_sock_create(&net->dccp.v4_ctl_sk, PF_INET, | 
|  | 1026 | SOCK_DCCP, IPPROTO_DCCP, net); | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1027 | } | 
|  | 1028 |  | 
| Alexey Dobriyan | 2c8c1e7 | 2010-01-17 03:35:32 +0000 | [diff] [blame] | 1029 | static void __net_exit dccp_v4_exit_net(struct net *net) | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1030 | { | 
| Pavel Emelyanov | b76c4b2 | 2008-04-13 22:29:59 -0700 | [diff] [blame] | 1031 | inet_ctl_sock_destroy(net->dccp.v4_ctl_sk); | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1032 | } | 
|  | 1033 |  | 
|  | 1034 | static struct pernet_operations dccp_v4_ops = { | 
|  | 1035 | .init	= dccp_v4_init_net, | 
|  | 1036 | .exit	= dccp_v4_exit_net, | 
|  | 1037 | }; | 
|  | 1038 |  | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1039 | static int __init dccp_v4_init(void) | 
|  | 1040 | { | 
|  | 1041 | int err = proto_register(&dccp_v4_prot, 1); | 
|  | 1042 |  | 
|  | 1043 | if (err != 0) | 
|  | 1044 | goto out; | 
|  | 1045 |  | 
|  | 1046 | err = inet_add_protocol(&dccp_v4_protocol, IPPROTO_DCCP); | 
|  | 1047 | if (err != 0) | 
|  | 1048 | goto out_proto_unregister; | 
|  | 1049 |  | 
|  | 1050 | inet_register_protosw(&dccp_v4_protosw); | 
|  | 1051 |  | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1052 | err = register_pernet_subsys(&dccp_v4_ops); | 
|  | 1053 | if (err) | 
|  | 1054 | goto out_destroy_ctl_sock; | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1055 | out: | 
|  | 1056 | return err; | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1057 | out_destroy_ctl_sock: | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1058 | inet_unregister_protosw(&dccp_v4_protosw); | 
|  | 1059 | inet_del_protocol(&dccp_v4_protocol, IPPROTO_DCCP); | 
|  | 1060 | out_proto_unregister: | 
|  | 1061 | proto_unregister(&dccp_v4_prot); | 
|  | 1062 | goto out; | 
|  | 1063 | } | 
|  | 1064 |  | 
|  | 1065 | static void __exit dccp_v4_exit(void) | 
|  | 1066 | { | 
| Pavel Emelyanov | 72a2d61 | 2008-04-13 22:29:13 -0700 | [diff] [blame] | 1067 | unregister_pernet_subsys(&dccp_v4_ops); | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1068 | inet_unregister_protosw(&dccp_v4_protosw); | 
|  | 1069 | inet_del_protocol(&dccp_v4_protocol, IPPROTO_DCCP); | 
|  | 1070 | proto_unregister(&dccp_v4_prot); | 
|  | 1071 | } | 
|  | 1072 |  | 
|  | 1073 | module_init(dccp_v4_init); | 
|  | 1074 | module_exit(dccp_v4_exit); | 
|  | 1075 |  | 
|  | 1076 | /* | 
|  | 1077 | * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33) | 
|  | 1078 | * values directly, Also cover the case where the protocol is not specified, | 
|  | 1079 | * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP | 
|  | 1080 | */ | 
| Jean Delvare | 7131c6c | 2007-10-21 16:45:03 -0700 | [diff] [blame] | 1081 | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 33, 6); | 
|  | 1082 | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 0, 6); | 
| Arnaldo Carvalho de Melo | b61fafc | 2006-03-20 21:25:11 -0800 | [diff] [blame] | 1083 | MODULE_LICENSE("GPL"); | 
|  | 1084 | MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); | 
|  | 1085 | MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); |