aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2012-06-19 21:56:21 -0400
committerDavid S. Miller <davem@davemloft.net>2012-06-19 21:56:21 -0400
commitf9242b6b28d61295f2bf7e8adfb1060b382e5381 (patch)
treeb395670bd6ae832e9f6f87f47a1840baf3a06d0d
parent677a3d60fb3153f786a0d28fcf0287670e7bd3c2 (diff)
inet: Sanitize inet{,6} protocol demux.
Don't pretend that inet_protos[] and inet6_protos[] are hashes, thay are just a straight arrays. Remove all unnecessary hash masking. Document MAX_INET_PROTOS. Use RAW_HTABLE_SIZE when appropriate. Reported-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/protocol.h7
-rw-r--r--net/ipv4/af_inet.c26
-rw-r--r--net/ipv4/icmp.c9
-rw-r--r--net/ipv4/ip_input.c5
-rw-r--r--net/ipv4/protocol.c8
-rw-r--r--net/ipv6/icmp.c7
-rw-r--r--net/ipv6/ip6_input.c9
-rw-r--r--net/ipv6/protocol.c8
-rw-r--r--net/ipv6/raw.c4
9 files changed, 36 insertions, 47 deletions
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 875f4895b033..a1b1b530c338 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -29,8 +29,11 @@
29#include <linux/ipv6.h> 29#include <linux/ipv6.h>
30#endif 30#endif
31 31
32#define MAX_INET_PROTOS 256 /* Must be a power of 2 */ 32/* This is one larger than the largest protocol value that can be
33 33 * found in an ipv4 or ipv6 header. Since in both cases the protocol
34 * value is presented in a __u8, this is defined to be 256.
35 */
36#define MAX_INET_PROTOS 256
34 37
35/* This is used to register protocols. */ 38/* This is used to register protocols. */
36struct net_protocol { 39struct net_protocol {
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index e4e8e00a2c91..85a3b1763136 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -242,20 +242,18 @@ void build_ehash_secret(void)
242} 242}
243EXPORT_SYMBOL(build_ehash_secret); 243EXPORT_SYMBOL(build_ehash_secret);
244 244
245static inline int inet_netns_ok(struct net *net, int protocol) 245static inline int inet_netns_ok(struct net *net, __u8 protocol)
246{ 246{
247 int hash;
248 const struct net_protocol *ipprot; 247 const struct net_protocol *ipprot;
249 248
250 if (net_eq(net, &init_net)) 249 if (net_eq(net, &init_net))
251 return 1; 250 return 1;
252 251
253 hash = protocol & (MAX_INET_PROTOS - 1); 252 ipprot = rcu_dereference(inet_protos[protocol]);
254 ipprot = rcu_dereference(inet_protos[hash]); 253 if (ipprot == NULL) {
255
256 if (ipprot == NULL)
257 /* raw IP is OK */ 254 /* raw IP is OK */
258 return 1; 255 return 1;
256 }
259 return ipprot->netns_ok; 257 return ipprot->netns_ok;
260} 258}
261 259
@@ -1216,8 +1214,8 @@ EXPORT_SYMBOL(inet_sk_rebuild_header);
1216 1214
1217static int inet_gso_send_check(struct sk_buff *skb) 1215static int inet_gso_send_check(struct sk_buff *skb)
1218{ 1216{
1219 const struct iphdr *iph;
1220 const struct net_protocol *ops; 1217 const struct net_protocol *ops;
1218 const struct iphdr *iph;
1221 int proto; 1219 int proto;
1222 int ihl; 1220 int ihl;
1223 int err = -EINVAL; 1221 int err = -EINVAL;
@@ -1236,7 +1234,7 @@ static int inet_gso_send_check(struct sk_buff *skb)
1236 __skb_pull(skb, ihl); 1234 __skb_pull(skb, ihl);
1237 skb_reset_transport_header(skb); 1235 skb_reset_transport_header(skb);
1238 iph = ip_hdr(skb); 1236 iph = ip_hdr(skb);
1239 proto = iph->protocol & (MAX_INET_PROTOS - 1); 1237 proto = iph->protocol;
1240 err = -EPROTONOSUPPORT; 1238 err = -EPROTONOSUPPORT;
1241 1239
1242 rcu_read_lock(); 1240 rcu_read_lock();
@@ -1253,8 +1251,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1253 netdev_features_t features) 1251 netdev_features_t features)
1254{ 1252{
1255 struct sk_buff *segs = ERR_PTR(-EINVAL); 1253 struct sk_buff *segs = ERR_PTR(-EINVAL);
1256 struct iphdr *iph;
1257 const struct net_protocol *ops; 1254 const struct net_protocol *ops;
1255 struct iphdr *iph;
1258 int proto; 1256 int proto;
1259 int ihl; 1257 int ihl;
1260 int id; 1258 int id;
@@ -1286,7 +1284,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1286 skb_reset_transport_header(skb); 1284 skb_reset_transport_header(skb);
1287 iph = ip_hdr(skb); 1285 iph = ip_hdr(skb);
1288 id = ntohs(iph->id); 1286 id = ntohs(iph->id);
1289 proto = iph->protocol & (MAX_INET_PROTOS - 1); 1287 proto = iph->protocol;
1290 segs = ERR_PTR(-EPROTONOSUPPORT); 1288 segs = ERR_PTR(-EPROTONOSUPPORT);
1291 1289
1292 rcu_read_lock(); 1290 rcu_read_lock();
@@ -1340,7 +1338,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
1340 goto out; 1338 goto out;
1341 } 1339 }
1342 1340
1343 proto = iph->protocol & (MAX_INET_PROTOS - 1); 1341 proto = iph->protocol;
1344 1342
1345 rcu_read_lock(); 1343 rcu_read_lock();
1346 ops = rcu_dereference(inet_protos[proto]); 1344 ops = rcu_dereference(inet_protos[proto]);
@@ -1398,11 +1396,11 @@ out:
1398 1396
1399static int inet_gro_complete(struct sk_buff *skb) 1397static int inet_gro_complete(struct sk_buff *skb)
1400{ 1398{
1401 const struct net_protocol *ops; 1399 __be16 newlen = htons(skb->len - skb_network_offset(skb));
1402 struct iphdr *iph = ip_hdr(skb); 1400 struct iphdr *iph = ip_hdr(skb);
1403 int proto = iph->protocol & (MAX_INET_PROTOS - 1); 1401 const struct net_protocol *ops;
1402 int proto = iph->protocol;
1404 int err = -ENOSYS; 1403 int err = -ENOSYS;
1405 __be16 newlen = htons(skb->len - skb_network_offset(skb));
1406 1404
1407 csum_replace2(&iph->check, iph->tot_len, newlen); 1405 csum_replace2(&iph->check, iph->tot_len, newlen);
1408 iph->tot_len = newlen; 1406 iph->tot_len = newlen;
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index e1caa1abe5d1..49a74cc79dc8 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -637,12 +637,12 @@ EXPORT_SYMBOL(icmp_send);
637 637
638static void icmp_unreach(struct sk_buff *skb) 638static void icmp_unreach(struct sk_buff *skb)
639{ 639{
640 const struct net_protocol *ipprot;
640 const struct iphdr *iph; 641 const struct iphdr *iph;
641 struct icmphdr *icmph; 642 struct icmphdr *icmph;
642 int hash, protocol;
643 const struct net_protocol *ipprot;
644 u32 info = 0;
645 struct net *net; 643 struct net *net;
644 u32 info = 0;
645 int protocol;
646 646
647 net = dev_net(skb_dst(skb)->dev); 647 net = dev_net(skb_dst(skb)->dev);
648 648
@@ -731,9 +731,8 @@ static void icmp_unreach(struct sk_buff *skb)
731 */ 731 */
732 raw_icmp_error(skb, protocol, info); 732 raw_icmp_error(skb, protocol, info);
733 733
734 hash = protocol & (MAX_INET_PROTOS - 1);
735 rcu_read_lock(); 734 rcu_read_lock();
736 ipprot = rcu_dereference(inet_protos[hash]); 735 ipprot = rcu_dereference(inet_protos[protocol]);
737 if (ipprot && ipprot->err_handler) 736 if (ipprot && ipprot->err_handler)
738 ipprot->err_handler(skb, info); 737 ipprot->err_handler(skb, info);
739 rcu_read_unlock(); 738 rcu_read_unlock();
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 8590144ca330..c4fe1d271131 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -198,14 +198,13 @@ static int ip_local_deliver_finish(struct sk_buff *skb)
198 rcu_read_lock(); 198 rcu_read_lock();
199 { 199 {
200 int protocol = ip_hdr(skb)->protocol; 200 int protocol = ip_hdr(skb)->protocol;
201 int hash, raw;
202 const struct net_protocol *ipprot; 201 const struct net_protocol *ipprot;
202 int raw;
203 203
204 resubmit: 204 resubmit:
205 raw = raw_local_deliver(skb, protocol); 205 raw = raw_local_deliver(skb, protocol);
206 206
207 hash = protocol & (MAX_INET_PROTOS - 1); 207 ipprot = rcu_dereference(inet_protos[protocol]);
208 ipprot = rcu_dereference(inet_protos[hash]);
209 if (ipprot != NULL) { 208 if (ipprot != NULL) {
210 int ret; 209 int ret;
211 210
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index 9ae5c01cd0b2..8918eff1426d 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -36,9 +36,7 @@ const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
36 36
37int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol) 37int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
38{ 38{
39 int hash = protocol & (MAX_INET_PROTOS - 1); 39 return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],
40
41 return !cmpxchg((const struct net_protocol **)&inet_protos[hash],
42 NULL, prot) ? 0 : -1; 40 NULL, prot) ? 0 : -1;
43} 41}
44EXPORT_SYMBOL(inet_add_protocol); 42EXPORT_SYMBOL(inet_add_protocol);
@@ -49,9 +47,9 @@ EXPORT_SYMBOL(inet_add_protocol);
49 47
50int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol) 48int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol)
51{ 49{
52 int ret, hash = protocol & (MAX_INET_PROTOS - 1); 50 int ret;
53 51
54 ret = (cmpxchg((const struct net_protocol **)&inet_protos[hash], 52 ret = (cmpxchg((const struct net_protocol **)&inet_protos[protocol],
55 prot, NULL) == prot) ? 0 : -1; 53 prot, NULL) == prot) ? 0 : -1;
56 54
57 synchronize_net(); 55 synchronize_net();
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 5247d5c211f9..c7da1422cbde 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -600,9 +600,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
600{ 600{
601 const struct inet6_protocol *ipprot; 601 const struct inet6_protocol *ipprot;
602 int inner_offset; 602 int inner_offset;
603 int hash;
604 u8 nexthdr;
605 __be16 frag_off; 603 __be16 frag_off;
604 u8 nexthdr;
606 605
607 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 606 if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
608 return; 607 return;
@@ -629,10 +628,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
629 --ANK (980726) 628 --ANK (980726)
630 */ 629 */
631 630
632 hash = nexthdr & (MAX_INET_PROTOS - 1);
633
634 rcu_read_lock(); 631 rcu_read_lock();
635 ipprot = rcu_dereference(inet6_protos[hash]); 632 ipprot = rcu_dereference(inet6_protos[nexthdr]);
636 if (ipprot && ipprot->err_handler) 633 if (ipprot && ipprot->err_handler)
637 ipprot->err_handler(skb, NULL, type, code, inner_offset, info); 634 ipprot->err_handler(skb, NULL, type, code, inner_offset, info);
638 rcu_read_unlock(); 635 rcu_read_unlock();
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 21a15dfe4a9e..5ab923e51af3 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -168,13 +168,12 @@ drop:
168 168
169static int ip6_input_finish(struct sk_buff *skb) 169static int ip6_input_finish(struct sk_buff *skb)
170{ 170{
171 struct net *net = dev_net(skb_dst(skb)->dev);
171 const struct inet6_protocol *ipprot; 172 const struct inet6_protocol *ipprot;
173 struct inet6_dev *idev;
172 unsigned int nhoff; 174 unsigned int nhoff;
173 int nexthdr; 175 int nexthdr;
174 bool raw; 176 bool raw;
175 u8 hash;
176 struct inet6_dev *idev;
177 struct net *net = dev_net(skb_dst(skb)->dev);
178 177
179 /* 178 /*
180 * Parse extension headers 179 * Parse extension headers
@@ -189,9 +188,7 @@ resubmit:
189 nexthdr = skb_network_header(skb)[nhoff]; 188 nexthdr = skb_network_header(skb)[nhoff];
190 189
191 raw = raw6_local_deliver(skb, nexthdr); 190 raw = raw6_local_deliver(skb, nexthdr);
192 191 if ((ipprot = rcu_dereference(inet6_protos[nexthdr])) != NULL) {
193 hash = nexthdr & (MAX_INET_PROTOS - 1);
194 if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {
195 int ret; 192 int ret;
196 193
197 if (ipprot->flags & INET6_PROTO_FINAL) { 194 if (ipprot->flags & INET6_PROTO_FINAL) {
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 9a7978fdc02a..053082dfc93e 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -29,9 +29,7 @@ const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly;
29 29
30int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol) 30int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol)
31{ 31{
32 int hash = protocol & (MAX_INET_PROTOS - 1); 32 return !cmpxchg((const struct inet6_protocol **)&inet6_protos[protocol],
33
34 return !cmpxchg((const struct inet6_protocol **)&inet6_protos[hash],
35 NULL, prot) ? 0 : -1; 33 NULL, prot) ? 0 : -1;
36} 34}
37EXPORT_SYMBOL(inet6_add_protocol); 35EXPORT_SYMBOL(inet6_add_protocol);
@@ -42,9 +40,9 @@ EXPORT_SYMBOL(inet6_add_protocol);
42 40
43int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol) 41int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol)
44{ 42{
45 int ret, hash = protocol & (MAX_INET_PROTOS - 1); 43 int ret;
46 44
47 ret = (cmpxchg((const struct inet6_protocol **)&inet6_protos[hash], 45 ret = (cmpxchg((const struct inet6_protocol **)&inet6_protos[protocol],
48 prot, NULL) == prot) ? 0 : -1; 46 prot, NULL) == prot) ? 0 : -1;
49 47
50 synchronize_net(); 48 synchronize_net();
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 43b0042f15f4..b5c1dcb27737 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -165,7 +165,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
165 saddr = &ipv6_hdr(skb)->saddr; 165 saddr = &ipv6_hdr(skb)->saddr;
166 daddr = saddr + 1; 166 daddr = saddr + 1;
167 167
168 hash = nexthdr & (MAX_INET_PROTOS - 1); 168 hash = nexthdr & (RAW_HTABLE_SIZE - 1);
169 169
170 read_lock(&raw_v6_hashinfo.lock); 170 read_lock(&raw_v6_hashinfo.lock);
171 sk = sk_head(&raw_v6_hashinfo.ht[hash]); 171 sk = sk_head(&raw_v6_hashinfo.ht[hash]);
@@ -229,7 +229,7 @@ bool raw6_local_deliver(struct sk_buff *skb, int nexthdr)
229{ 229{
230 struct sock *raw_sk; 230 struct sock *raw_sk;
231 231
232 raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (MAX_INET_PROTOS - 1)]); 232 raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (RAW_HTABLE_SIZE - 1)]);
233 if (raw_sk && !ipv6_raw_deliver(skb, nexthdr)) 233 if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
234 raw_sk = NULL; 234 raw_sk = NULL;
235 235