diff options
author | Jan Engelhardt <jengelh@medozas.de> | 2010-04-13 09:28:11 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-04-13 09:28:11 -0400 |
commit | 9e50849054a4824f06c66d2b449de21b98e03770 (patch) | |
tree | c5b48b8786af6111373d8646c5920c67a7002b97 /net | |
parent | 9f93ff5be54108066372d1c4100c515d9d9acc1b (diff) |
netfilter: ipv6: move POSTROUTING invocation before fragmentation
Patrick McHardy notes: "We used to invoke IPv4 POST_ROUTING after
fragmentation as well just to defragment the packets in conntrack
immediately afterwards, but that got changed during the
netfilter-ipsec integration. Ideally IPv6 would behave like IPv4."
This patch makes it so. Sending an oversized frame (e.g. `ping6
-s64000 -c1 ::1`) will now show up in POSTROUTING as a single skb
rather than multiple ones.
Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ip6_output.c | 49 |
1 files changed, 23 insertions, 26 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 4535b7a0169b..236ac7813744 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -82,22 +82,6 @@ int ip6_local_out(struct sk_buff *skb) | |||
82 | } | 82 | } |
83 | EXPORT_SYMBOL_GPL(ip6_local_out); | 83 | EXPORT_SYMBOL_GPL(ip6_local_out); |
84 | 84 | ||
85 | static int ip6_output_finish(struct sk_buff *skb) | ||
86 | { | ||
87 | struct dst_entry *dst = skb_dst(skb); | ||
88 | |||
89 | if (dst->hh) | ||
90 | return neigh_hh_output(dst->hh, skb); | ||
91 | else if (dst->neighbour) | ||
92 | return dst->neighbour->output(skb); | ||
93 | |||
94 | IP6_INC_STATS_BH(dev_net(dst->dev), | ||
95 | ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); | ||
96 | kfree_skb(skb); | ||
97 | return -EINVAL; | ||
98 | |||
99 | } | ||
100 | |||
101 | /* dev_loopback_xmit for use with netfilter. */ | 85 | /* dev_loopback_xmit for use with netfilter. */ |
102 | static int ip6_dev_loopback_xmit(struct sk_buff *newskb) | 86 | static int ip6_dev_loopback_xmit(struct sk_buff *newskb) |
103 | { | 87 | { |
@@ -111,8 +95,7 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb) | |||
111 | return 0; | 95 | return 0; |
112 | } | 96 | } |
113 | 97 | ||
114 | 98 | static int ip6_finish_output2(struct sk_buff *skb) | |
115 | static int ip6_output2(struct sk_buff *skb) | ||
116 | { | 99 | { |
117 | struct dst_entry *dst = skb_dst(skb); | 100 | struct dst_entry *dst = skb_dst(skb); |
118 | struct net_device *dev = dst->dev; | 101 | struct net_device *dev = dst->dev; |
@@ -150,8 +133,15 @@ static int ip6_output2(struct sk_buff *skb) | |||
150 | skb->len); | 133 | skb->len); |
151 | } | 134 | } |
152 | 135 | ||
153 | return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, skb->dev, | 136 | if (dst->hh) |
154 | ip6_output_finish); | 137 | return neigh_hh_output(dst->hh, skb); |
138 | else if (dst->neighbour) | ||
139 | return dst->neighbour->output(skb); | ||
140 | |||
141 | IP6_INC_STATS_BH(dev_net(dst->dev), | ||
142 | ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); | ||
143 | kfree_skb(skb); | ||
144 | return -EINVAL; | ||
155 | } | 145 | } |
156 | 146 | ||
157 | static inline int ip6_skb_dst_mtu(struct sk_buff *skb) | 147 | static inline int ip6_skb_dst_mtu(struct sk_buff *skb) |
@@ -162,21 +152,28 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb) | |||
162 | skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb)); | 152 | skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb)); |
163 | } | 153 | } |
164 | 154 | ||
155 | static int ip6_finish_output(struct sk_buff *skb) | ||
156 | { | ||
157 | if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || | ||
158 | dst_allfrag(skb_dst(skb))) | ||
159 | return ip6_fragment(skb, ip6_finish_output2); | ||
160 | else | ||
161 | return ip6_finish_output2(skb); | ||
162 | } | ||
163 | |||
165 | int ip6_output(struct sk_buff *skb) | 164 | int ip6_output(struct sk_buff *skb) |
166 | { | 165 | { |
166 | struct net_device *dev = skb_dst(skb)->dev; | ||
167 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); | 167 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); |
168 | if (unlikely(idev->cnf.disable_ipv6)) { | 168 | if (unlikely(idev->cnf.disable_ipv6)) { |
169 | IP6_INC_STATS(dev_net(skb_dst(skb)->dev), idev, | 169 | IP6_INC_STATS(dev_net(dev), idev, |
170 | IPSTATS_MIB_OUTDISCARDS); | 170 | IPSTATS_MIB_OUTDISCARDS); |
171 | kfree_skb(skb); | 171 | kfree_skb(skb); |
172 | return 0; | 172 | return 0; |
173 | } | 173 | } |
174 | 174 | ||
175 | if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || | 175 | return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev, |
176 | dst_allfrag(skb_dst(skb))) | 176 | ip6_finish_output); |
177 | return ip6_fragment(skb, ip6_output2); | ||
178 | else | ||
179 | return ip6_output2(skb); | ||
180 | } | 177 | } |
181 | 178 | ||
182 | /* | 179 | /* |