Commit 9e508490 authored by Jan Engelhardt's avatar Jan Engelhardt Committed by Patrick McHardy

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: default avatarJan Engelhardt <jengelh@medozas.de>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 9f93ff5b
...@@ -82,22 +82,6 @@ int ip6_local_out(struct sk_buff *skb) ...@@ -82,22 +82,6 @@ int ip6_local_out(struct sk_buff *skb)
} }
EXPORT_SYMBOL_GPL(ip6_local_out); EXPORT_SYMBOL_GPL(ip6_local_out);
static int ip6_output_finish(struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
if (dst->hh)
return neigh_hh_output(dst->hh, skb);
else if (dst->neighbour)
return dst->neighbour->output(skb);
IP6_INC_STATS_BH(dev_net(dst->dev),
ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
return -EINVAL;
}
/* dev_loopback_xmit for use with netfilter. */ /* dev_loopback_xmit for use with netfilter. */
static int ip6_dev_loopback_xmit(struct sk_buff *newskb) static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
{ {
...@@ -111,8 +95,7 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb) ...@@ -111,8 +95,7 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
return 0; return 0;
} }
static int ip6_finish_output2(struct sk_buff *skb)
static int ip6_output2(struct sk_buff *skb)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct net_device *dev = dst->dev; struct net_device *dev = dst->dev;
...@@ -150,8 +133,15 @@ static int ip6_output2(struct sk_buff *skb) ...@@ -150,8 +133,15 @@ static int ip6_output2(struct sk_buff *skb)
skb->len); skb->len);
} }
return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, skb->dev, if (dst->hh)
ip6_output_finish); return neigh_hh_output(dst->hh, skb);
else if (dst->neighbour)
return dst->neighbour->output(skb);
IP6_INC_STATS_BH(dev_net(dst->dev),
ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
return -EINVAL;
} }
static inline int ip6_skb_dst_mtu(struct sk_buff *skb) 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,21 +152,28 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb)); skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
} }
static int ip6_finish_output(struct sk_buff *skb)
{
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)))
return ip6_fragment(skb, ip6_finish_output2);
else
return ip6_finish_output2(skb);
}
int ip6_output(struct sk_buff *skb) int ip6_output(struct sk_buff *skb)
{ {
struct net_device *dev = skb_dst(skb)->dev;
struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
if (unlikely(idev->cnf.disable_ipv6)) { if (unlikely(idev->cnf.disable_ipv6)) {
IP6_INC_STATS(dev_net(skb_dst(skb)->dev), idev, IP6_INC_STATS(dev_net(dev), idev,
IPSTATS_MIB_OUTDISCARDS); IPSTATS_MIB_OUTDISCARDS);
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
} }
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev,
dst_allfrag(skb_dst(skb))) ip6_finish_output);
return ip6_fragment(skb, ip6_output2);
else
return ip6_output2(skb);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment