diff options
author | David Miller <davem@davemloft.net> | 2015-04-05 22:19:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-04-07 15:25:55 -0400 |
commit | 7026b1ddb6b8d4e6ee33dc2bd06c0ca8746fa7ab (patch) | |
tree | 3e11ed0f186ea6066a3f7efecb88d85bc732ee51 /net/ipv6/ip6_output.c | |
parent | 1c984f8a5df085bcf35364a8a870bd4db4da4ed3 (diff) |
netfilter: Pass socket pointer down through okfn().
On the output paths in particular, we have to sometimes deal with two
socket contexts. First, and usually skb->sk, is the local socket that
generated the frame.
And second, is potentially the socket used to control a tunneling
socket, such as one the encapsulates using UDP.
We do not want to disassociate skb->sk when encapsulating in order
to fix this, because that would break socket memory accounting.
The most extreme case where this can cause huge problems is an
AF_PACKET socket transmitting over a vxlan device. We hit code
paths doing checks that assume they are dealing with an ipv4
socket, but are actually operating upon the AF_PACKET one.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 33 |
1 files changed, 18 insertions, 15 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 654f245aa930..7fde1f265c90 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -56,7 +56,7 @@ | |||
56 | #include <net/checksum.h> | 56 | #include <net/checksum.h> |
57 | #include <linux/mroute6.h> | 57 | #include <linux/mroute6.h> |
58 | 58 | ||
59 | static int ip6_finish_output2(struct sk_buff *skb) | 59 | static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb) |
60 | { | 60 | { |
61 | struct dst_entry *dst = skb_dst(skb); | 61 | struct dst_entry *dst = skb_dst(skb); |
62 | struct net_device *dev = dst->dev; | 62 | struct net_device *dev = dst->dev; |
@@ -70,7 +70,7 @@ static int ip6_finish_output2(struct sk_buff *skb) | |||
70 | if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { | 70 | if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { |
71 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); | 71 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); |
72 | 72 | ||
73 | if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) && | 73 | if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && |
74 | ((mroute6_socket(dev_net(dev), skb) && | 74 | ((mroute6_socket(dev_net(dev), skb) && |
75 | !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || | 75 | !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || |
76 | ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, | 76 | ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, |
@@ -82,7 +82,7 @@ static int ip6_finish_output2(struct sk_buff *skb) | |||
82 | */ | 82 | */ |
83 | if (newskb) | 83 | if (newskb) |
84 | NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, | 84 | NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, |
85 | newskb, NULL, newskb->dev, | 85 | sk, newskb, NULL, newskb->dev, |
86 | dev_loopback_xmit); | 86 | dev_loopback_xmit); |
87 | 87 | ||
88 | if (ipv6_hdr(skb)->hop_limit == 0) { | 88 | if (ipv6_hdr(skb)->hop_limit == 0) { |
@@ -122,14 +122,14 @@ static int ip6_finish_output2(struct sk_buff *skb) | |||
122 | return -EINVAL; | 122 | return -EINVAL; |
123 | } | 123 | } |
124 | 124 | ||
125 | static int ip6_finish_output(struct sk_buff *skb) | 125 | static int ip6_finish_output(struct sock *sk, struct sk_buff *skb) |
126 | { | 126 | { |
127 | if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || | 127 | if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || |
128 | dst_allfrag(skb_dst(skb)) || | 128 | dst_allfrag(skb_dst(skb)) || |
129 | (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) | 129 | (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) |
130 | return ip6_fragment(skb, ip6_finish_output2); | 130 | return ip6_fragment(sk, skb, ip6_finish_output2); |
131 | else | 131 | else |
132 | return ip6_finish_output2(skb); | 132 | return ip6_finish_output2(sk, skb); |
133 | } | 133 | } |
134 | 134 | ||
135 | int ip6_output(struct sock *sk, struct sk_buff *skb) | 135 | int ip6_output(struct sock *sk, struct sk_buff *skb) |
@@ -143,7 +143,8 @@ int ip6_output(struct sock *sk, struct sk_buff *skb) | |||
143 | return 0; | 143 | return 0; |
144 | } | 144 | } |
145 | 145 | ||
146 | return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev, | 146 | return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb, |
147 | NULL, dev, | ||
147 | ip6_finish_output, | 148 | ip6_finish_output, |
148 | !(IP6CB(skb)->flags & IP6SKB_REROUTED)); | 149 | !(IP6CB(skb)->flags & IP6SKB_REROUTED)); |
149 | } | 150 | } |
@@ -223,8 +224,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, | |||
223 | if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) { | 224 | if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) { |
224 | IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)), | 225 | IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)), |
225 | IPSTATS_MIB_OUT, skb->len); | 226 | IPSTATS_MIB_OUT, skb->len); |
226 | return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, | 227 | return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb, |
227 | dst->dev, dst_output); | 228 | NULL, dst->dev, dst_output_sk); |
228 | } | 229 | } |
229 | 230 | ||
230 | skb->dev = dst->dev; | 231 | skb->dev = dst->dev; |
@@ -316,10 +317,10 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) | |||
316 | return 0; | 317 | return 0; |
317 | } | 318 | } |
318 | 319 | ||
319 | static inline int ip6_forward_finish(struct sk_buff *skb) | 320 | static inline int ip6_forward_finish(struct sock *sk, struct sk_buff *skb) |
320 | { | 321 | { |
321 | skb_sender_cpu_clear(skb); | 322 | skb_sender_cpu_clear(skb); |
322 | return dst_output(skb); | 323 | return dst_output_sk(sk, skb); |
323 | } | 324 | } |
324 | 325 | ||
325 | static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst) | 326 | static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst) |
@@ -511,7 +512,8 @@ int ip6_forward(struct sk_buff *skb) | |||
511 | 512 | ||
512 | IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); | 513 | IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); |
513 | IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len); | 514 | IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len); |
514 | return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev, | 515 | return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb, |
516 | skb->dev, dst->dev, | ||
515 | ip6_forward_finish); | 517 | ip6_forward_finish); |
516 | 518 | ||
517 | error: | 519 | error: |
@@ -538,7 +540,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) | |||
538 | skb_copy_secmark(to, from); | 540 | skb_copy_secmark(to, from); |
539 | } | 541 | } |
540 | 542 | ||
541 | int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | 543 | int ip6_fragment(struct sock *sk, struct sk_buff *skb, |
544 | int (*output)(struct sock *, struct sk_buff *)) | ||
542 | { | 545 | { |
543 | struct sk_buff *frag; | 546 | struct sk_buff *frag; |
544 | struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); | 547 | struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); |
@@ -667,7 +670,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) | |||
667 | ip6_copy_metadata(frag, skb); | 670 | ip6_copy_metadata(frag, skb); |
668 | } | 671 | } |
669 | 672 | ||
670 | err = output(skb); | 673 | err = output(sk, skb); |
671 | if (!err) | 674 | if (!err) |
672 | IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), | 675 | IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), |
673 | IPSTATS_MIB_FRAGCREATES); | 676 | IPSTATS_MIB_FRAGCREATES); |
@@ -800,7 +803,7 @@ slow_path: | |||
800 | /* | 803 | /* |
801 | * Put this fragment into the sending queue. | 804 | * Put this fragment into the sending queue. |
802 | */ | 805 | */ |
803 | err = output(frag); | 806 | err = output(sk, frag); |
804 | if (err) | 807 | if (err) |
805 | goto fail; | 808 | goto fail; |
806 | 809 | ||