net: improve the process of redirect and toobig for ipv6 tunnels
Subsystems (3)
cover net: improve the process of redirect and toobig for ipv6 tunnels
Now let's say there are 3 kinds of icmp packets to process for tunnels, toobig(needfrag), redirect, others, their process should be:
- toobig(needfrag)
update the lower dst's pmtu by route cache, also update sk dst's pmtu if possible, or it will be fine if sk dst pmtu will get updated on tx path.
- redirect
update the lower dst's gw by route cache and return, no need to send this redirect packet to user sk.
- others
send the packet to user's sk, or it will also be fine to use err_count to count it and report fail link on tx path.
All ipv4 tunnels basically follow this while some of ipv6 tunnels are doing in different ways, like ip6gre and ip6_tunnels update tnl dev's mtu instead of updating lower dst pmtu, no redirect process on their err_handlers, which doesn't make any sense and even causes performance problems.
This patchset is to improve the process of redirect and toobig for ip6gre ip4ip6, ip6ip6 tunnels, as in ipv4 tunnels.
Xin Long (5):
ip6_gre: add the process for redirect in ip6gre_err
ip6_gre: process toobig in a better way
ip6_tunnel: add the process for redirect in ip6_tnl_err
ip6_tunnel: process toobig in a better way
ip6_tunnel: clean up ip4ip6 and ip6ip6's err_handlers
net/ipv6/ip6_gre.c | 20 ++++++---------- net/ipv6/ip6_tunnel.c | 64 ++++++++++++++++++++++----------------------------- 2 files changed, 34 insertions(+), 50 deletions(-)
Signature · 1 line
2.1.0
PATCH 1/5 ip6_gre: add the process for redirect in ip6gre_err
This patch is to add redirect icmp packet process for ip6gre by calling ip6_redirect() in ip6gre_err(), as in vti6_err.
Prior to this patch, there's even no route cache generated after receiving redirect.
--- net/ipv6/ip6_gre.c | 5 +++++ 1 file changed, 5 insertions(+)
net/ipv6/ip6_gre.c +5 -0
@@ -369,6 +369,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)static void ip6gre_err (struct sk_buff *skb,struct inet6_skb_parm *opt,u8 type,u8 code,int offset,__be32 info) { +struct net *net =dev_net (skb->dev);const struct gre_base_hdr *greh;const struct ipv6hdr *ipv6h;int grehlen =sizeof (*greh);
@@ -442,6 +443,10 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, mtu =IPV6_MIN_MTU ; t->dev->mtu = mtu;return ; +case NDISC_REDIRECT : +ip6_redirect (skb, net, skb->dev->ifindex,0 , +sock_net_uid (net,NULL )); +return ; }if (time_before (jiffies, t->err_time +IP6TUNNEL_ERR_TIMEO ))
Signature · 1 line
2.1.0
PATCH 2/5 ip6_gre: process toobig in a better way
Now ip6gre processes toobig icmp packet by setting gre dev's mtu in ip6gre_err, which would cause few things not good:
- It couldn't set mtu with dev_set_mtu due to it's not in user context,
which causes route cache and idev->cnf.mtu6 not to be updated.
- It has to update sk dst pmtu in tx path according to gredev->mtu for ip6gre, while it updates pmtu again according to lower dst pmtu in ip6_tnl_xmit.
- To change dev->mtu by toobig icmp packet is not a good idea, it should only work on pmtu.
This patch is to process toobig by updating the lower dst's pmtu, as later sk dst pmtu will be updated in ip6_tnl_xmit, the same way as in ip4gre.
Note that gre dev's mtu will not be updated any more, it doesn't make any sense to change dev's mtu after receiving a toobig packet.
--- net/ipv6/ip6_gre.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-)
net/ipv6/ip6_gre.c +2 -13
@@ -403,9 +403,8 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,return ;switch (type) { -__u32 teli;struct ipv6_tlv_tnl_enc_lim *tel; -__u32 mtu; +__u32 teli;case ICMPV6_DEST_UNREACH :net_dbg_ratelimited ("%s: Path to destination invalid or inactive!\n" , t->parms.name);
@@ -436,12 +435,7 @@ static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, }return ;case ICMPV6_PKT_TOOBIG : - mtu =be32_to_cpu (info) - offset - t->tun_hlen; -if (t->dev->type ==ARPHRD_ETHER ) - mtu -=ETH_HLEN ; -if (mtu <IPV6_MIN_MTU ) - mtu =IPV6_MIN_MTU ; - t->dev->mtu = mtu; +ip6_update_pmtu (skb, net, info,0 ,0 ,sock_net_uid (net,NULL ));return ;case NDISC_REDIRECT : ip6_redirect(skb, net, skb->dev->ifindex, 0,
@@ -508,7 +502,6 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb, __u32 *pmtu, __be16 proto) {struct ip6_tnl *tunnel =netdev_priv (dev); -struct dst_entry *dst =skb_dst (skb);__be16 protocol;if (dev->type ==ARPHRD_ETHER )
@@ -527,10 +520,6 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,gre_build_header (skb, tunnel->tun_hlen, tunnel->parms.o_flags, protocol, tunnel->parms.o_key,htonl (tunnel->o_seqno)); -/* TooBig packet may have updated dst->dev's mtu */ -if (dst &&dst_mtu (dst) > dst->dev->mtu) - dst->ops->update_pmtu (dst,NULL , skb, dst->dev->mtu); -return ip6_tnl_xmit (skb, dev, dsfield, fl6, encap_limit, pmtu,NEXTHDR_GRE ); }
Signature · 1 line
2.1.0
PATCH 3/5 ip6_tunnel: add the process for redirect in ip6_tnl_err
The same process for redirect in "ip6_gre: add the process for redirect in ip6gre_err" is needed by ip4ip6 and ip6ip6 as well.
--- net/ipv6/ip6_tunnel.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
net/ipv6/ip6_tunnel.c +10 -5
@@ -471,15 +471,16 @@ static int ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, u8 *type, u8 *code, int *msg, __u32 *info, int offset) { - const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) skb->data; -struct ip6_tnl *t; -int rel_msg =0 ; + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data; +struct net *net =dev_net (skb->dev);u8 rel_type =ICMPV6_DEST_UNREACH ;u8 rel_code =ICMPV6_ADDR_UNREACH ; -u8 tproto;__u32 rel_info =0 ; -__u16 len; +struct ip6_tnl *t;int err = -ENOENT ; +int rel_msg =0 ; +u8 tproto; +__u16 len;/* If the packet doesn't contain the original IPv6 header we are in trouble since we might need the source address for further
@@ -543,6 +544,10 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, rel_msg =1 ; }break ; +case NDISC_REDIRECT : +ip6_redirect (skb, net, skb->dev->ifindex,0 , +sock_net_uid (net,NULL )); +break ; } *type = rel_type;
Signature · 1 line
2.1.0
PATCH 4/5 ip6_tunnel: process toobig in a better way
The same improvement in "ip6_gre: process toobig in a better way" is needed by ip4ip6 and ip6ip6 as well.
Note that ip4ip6 and ip6ip6 will also update sk dst pmtu in their err_handlers. Like I said before, gre6 could not do this as it's inner proto is not certain. But for all of them, sk dst pmtu will be updated in tx path if in need.
--- net/ipv6/ip6_tunnel.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
net/ipv6/ip6_tunnel.c +3 -4
@@ -498,9 +498,8 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, err =0 ;switch (*type) { -__u32 teli;struct ipv6_tlv_tnl_enc_lim *tel; -__u32 mtu; +__u32 mtu, teli;case ICMPV6_DEST_UNREACH :net_dbg_ratelimited ("%s: Path to destination invalid or inactive!\n" , t->parms.name);
@@ -531,11 +530,11 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, }break ;case ICMPV6_PKT_TOOBIG : +ip6_update_pmtu (skb, net,htonl (*info),0 ,0 , +sock_net_uid (net,NULL )); mtu = *info - offset;if (mtu <IPV6_MIN_MTU ) mtu =IPV6_MIN_MTU ; - t->dev->mtu = mtu; - len =sizeof (*ipv6h) +ntohs (ipv6h->payload_len);if (len > mtu) { rel_type =ICMPV6_PKT_TOOBIG ;
Signature · 1 line
2.1.0
PATCH 5/5 ip6_tunnel: clean up ip4ip6 and ip6ip6's err_handlers
This patch is to remove some useless codes of redirect and fix some indents on ip4ip6 and ip6ip6's err_handlers.
Note that redirect icmp packet is already processed in ip6_tnl_err, the old redirect codes in ip4ip6_err actually never worked even before this patch. Besides, there's no need to send redirect to user's sk, it's for lower dst, so just remove it in this patch.
--- net/ipv6/ip6_tunnel.c | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-)
net/ipv6/ip6_tunnel.c +14 -28
@@ -563,13 +563,12 @@ static int ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { - int rel_msg = 0; -u8 rel_type = type; -u8 rel_code = code; __u32 rel_info = ntohl(info); -int err; -struct sk_buff *skb2;const struct iphdr *eiph; +struct sk_buff *skb2; +int err, rel_msg =0 ; +u8 rel_type = type; +u8 rel_code = code;struct rtable *rt;struct flowi4 fl4;
@@ -594,10 +593,6 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, rel_type =ICMP_DEST_UNREACH ; rel_code =ICMP_FRAG_NEEDED ;break ; -case NDISC_REDIRECT : - rel_type =ICMP_REDIRECT ; - rel_code =ICMP_REDIR_HOST ; -/* fall through */ default :return 0 ; }
@@ -616,33 +611,26 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, eiph =ip_hdr (skb2);/* Try to guess incoming interface */ - rt =ip_route_output_ports (dev_net (skb->dev), &fl4,NULL , - eiph->saddr,0 , -0 ,0 , -IPPROTO_IPIP ,RT_TOS (eiph->tos),0 ); + rt =ip_route_output_ports (dev_net (skb->dev), &fl4,NULL , eiph->saddr, +0 ,0 ,0 ,IPPROTO_IPIP ,RT_TOS (eiph->tos),0 );if (IS_ERR (rt)) goto out; skb2->dev = rt->dst.dev; +ip_rt_put (rt);/* route "incoming" packet */ if (rt->rt_flags &RTCF_LOCAL ) { -ip_rt_put (rt); - rt =NULL ; rt =ip_route_output_ports (dev_net (skb->dev), &fl4,NULL , - eiph->daddr, eiph->saddr, -0 ,0 , -IPPROTO_IPIP , -RT_TOS (eiph->tos),0 ); -if (IS_ERR (rt) || - rt->dst.dev->type !=ARPHRD_TUNNEL ) { + eiph->daddr, eiph->saddr,0 ,0 , +IPPROTO_IPIP ,RT_TOS (eiph->tos),0 ); +if (IS_ERR (rt) || rt->dst.dev->type !=ARPHRD_TUNNEL ) {if (!IS_ERR (rt))ip_rt_put (rt); goto out; }skb_dst_set (skb2, &rt->dst); }else { -ip_rt_put (rt);if (ip_route_input (skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||skb_dst (skb2)->dev->type !=ARPHRD_TUNNEL )
@@ -654,10 +642,9 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,if (rel_info >dst_mtu (skb_dst (skb2))) goto out; -skb_dst (skb2)->ops->update_pmtu (skb_dst (skb2),NULL , skb2, rel_info); +skb_dst (skb2)->ops->update_pmtu (skb_dst (skb2),NULL , skb2, + rel_info); } -if (rel_type ==ICMP_REDIRECT ) -skb_dst (skb2)->ops->redirect (skb_dst (skb2),NULL , skb2);icmp_send (skb2, rel_type, rel_code,htonl (rel_info));
@@ -670,11 +657,10 @@ static int ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { - int rel_msg = 0; + __u32 rel_info = ntohl(info); +int err, rel_msg =0 ;u8 rel_type = type;u8 rel_code = code; -__u32 rel_info =ntohl (info); -int err; err =ip6_tnl_err (skb,IPPROTO_IPV6 , opt, &rel_type, &rel_code, &rel_msg, &rel_info, offset);
Signature · 1 line
2.1.0
David Miller
maintainerFrom: Xin Long <[email protected]>
Date: Sat, 11 Nov 2017 19:06:48 +0800
Quoted 16 lines
Now let's say there are 3 kinds of icmp packets to process for tunnels, toobig(needfrag), redirect, others, their process should be:
- toobig(needfrag)
update the lower dst's pmtu by route cache, also update sk dst's pmtu if possible, or it will be fine if sk dst pmtu will get updated on tx path.- redirect
update the lower dst's gw by route cache and return, no need to send this redirect packet to user sk.- others
send the packet to user's sk, or it will also be fine to useerr_countto count it and report fail link on tx path.
Quoted 8 lines
All ipv4 tunnels basically follow this while some of ipv6 tunnels are doing in different ways, like ip6gre and
ip6_tunnelsupdate tnl dev's mtu instead of updating lower dst pmtu, no redirect process on theirerr_handlers, which doesn't make any sense and even causes performance problems.This patchset is to improve the process of redirect and toobig for ip6gre ip4ip6, ip6ip6 tunnels, as in ipv4 tunnels.
Series applied, thank you.