diff options
author | David S. Miller <davem@davemloft.net> | 2012-06-19 21:56:21 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-19 21:56:21 -0400 |
commit | f9242b6b28d61295f2bf7e8adfb1060b382e5381 (patch) | |
tree | b395670bd6ae832e9f6f87f47a1840baf3a06d0d | |
parent | 677a3d60fb3153f786a0d28fcf0287670e7bd3c2 (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.h | 7 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 26 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 9 | ||||
-rw-r--r-- | net/ipv4/ip_input.c | 5 | ||||
-rw-r--r-- | net/ipv4/protocol.c | 8 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 7 | ||||
-rw-r--r-- | net/ipv6/ip6_input.c | 9 | ||||
-rw-r--r-- | net/ipv6/protocol.c | 8 | ||||
-rw-r--r-- | net/ipv6/raw.c | 4 |
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. */ |
36 | struct net_protocol { | 39 | struct 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 | } |
243 | EXPORT_SYMBOL(build_ehash_secret); | 243 | EXPORT_SYMBOL(build_ehash_secret); |
244 | 244 | ||
245 | static inline int inet_netns_ok(struct net *net, int protocol) | 245 | static 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 | ||
1217 | static int inet_gso_send_check(struct sk_buff *skb) | 1215 | static 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 | ||
1399 | static int inet_gro_complete(struct sk_buff *skb) | 1397 | static 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 | ||
638 | static void icmp_unreach(struct sk_buff *skb) | 638 | static 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 | ||
37 | int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol) | 37 | int 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 | } |
44 | EXPORT_SYMBOL(inet_add_protocol); | 42 | EXPORT_SYMBOL(inet_add_protocol); |
@@ -49,9 +47,9 @@ EXPORT_SYMBOL(inet_add_protocol); | |||
49 | 47 | ||
50 | int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol) | 48 | int 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 | ||
169 | static int ip6_input_finish(struct sk_buff *skb) | 169 | static 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 | ||
30 | int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol) | 30 | int 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 | } |
37 | EXPORT_SYMBOL(inet6_add_protocol); | 35 | EXPORT_SYMBOL(inet6_add_protocol); |
@@ -42,9 +40,9 @@ EXPORT_SYMBOL(inet6_add_protocol); | |||
42 | 40 | ||
43 | int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol) | 41 | int 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 | ||