ipv6: IPsec pmtu/fragmentation fixes
Subsystems (4)
Steffen Klassert
maintainercover ipv6: IPsec pmtu/fragmentation fixes
This patchset fixes two issues for IPsec on ipv6. These issues are arround, basically forever.
The patchset is based on current net-next/master.
1) Fix for a IPv6 IPsec slowpath fragmentation problem. Already fixed for IPv4 with
git commit 353e5c9 (ipv4: Fix IPsec slowpath fragmentation problem).
2) Fix an incorrect update of the path mtu on IPv6 IPsec. Already fixed for IPv4 with
git commit b00897b (xfrm4: Don't call icmp_send on local error).
Steffen Klassert
maintainerPATCH 1/2 ipv6: Fix IPsec slowpath fragmentation problem
ip6_append_data() builds packets based on the mtu from dst_mtu(rt->dst.path). On IPsec the effective mtu is lower because we need to add the protocol headers and trailers later when we do the IPsec transformations. So after the IPsec transformations the packet might be too big, which leads to a slowpath fragmentation then. This patch fixes this by building the packets based on the lower IPsec mtu from dst_mtu(&rt->dst) and adapts the exthdr handling to this.
--- net/ipv6/ip6_output.c | 18 ++++++++++++------ net/ipv6/raw.c | 3 +-- 2 files changed, 13 insertions(+), 8 deletions(-)
net/ipv6/ip6_output.c +12 -6
@@ -1193,6 +1193,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,struct sk_buff *skb;unsigned int maxfraglen, fragheaderlen;int exthdrlen; +int dst_exthdrlen;int hh_len;int mtu;int copy;
@@ -1248,7 +1249,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, np->cork.hop_limit = hlimit; np->cork.tclass = tclass; mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? - rt->dst.dev->mtu :dst_mtu (rt->dst.path); + rt->dst.dev->mtu :dst_mtu (&rt->dst);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1259,16 +1260,17 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, cork->length =0 ; sk->sk_sndmsg_page =NULL ; sk->sk_sndmsg_off =0 ; - exthdrlen = rt->dst.header_len + (opt ? opt->opt_flen :0 ) - - rt->rt6i_nfheader_len; + exthdrlen = (opt ? opt->opt_flen :0 ) - rt->rt6i_nfheader_len; length += exthdrlen; transhdrlen += exthdrlen; + dst_exthdrlen = rt->dst.header_len; }else { rt = (struct rt6_info *)cork->dst; fl6 = &inet->cork.fl.u.ip6; opt = np->cork.opt; transhdrlen =0 ; exthdrlen =0 ; + dst_exthdrlen =0 ; mtu = cork->fragsize; }
@@ -1368,6 +1370,8 @@ alloc_new_skb:else alloclen = datalen + fragheaderlen; + alloclen += dst_exthdrlen; +/* * The last fragment gets additional space at tail. * Note: we overallocate on fragments with MSG_MODE
@@ -1419,9 +1423,9 @@ alloc_new_skb:/* * Find where to start putting bytes */ - data =skb_put (skb, fraglen); -skb_set_network_header (skb, exthdrlen); - data += fragheaderlen; + data =skb_put (skb, fraglen + dst_exthdrlen); +skb_set_network_header (skb, exthdrlen + dst_exthdrlen); + data += fragheaderlen + dst_exthdrlen; skb->transport_header = (skb->network_header + fragheaderlen);if (fraggap) {
@@ -1434,6 +1438,7 @@ alloc_new_skb:pskb_trim_unique (skb_prev, maxfraglen); } copy = datalen - transhdrlen - fraggap; +if (copy <0 ) { err = -EINVAL ;kfree_skb (skb);
@@ -1448,6 +1453,7 @@ alloc_new_skb: length -= datalen - fraggap; transhdrlen =0 ; exthdrlen =0 ; + dst_exthdrlen =0 ; csummode =CHECKSUM_NONE ;/*
net/ipv6/raw.c +1 -2
@@ -542,8 +542,7 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, goto out; offset = rp->offset; - total_len =inet_sk (sk)->cork.base.length - (skb_network_header (skb) - - skb->data); + total_len =inet_sk (sk)->cork.base.length;if (offset >= total_len -1 ) { err = -EINVAL ;ip6_flush_pending_frames (sk);
Signature · 1 line
1.7.0.4
Re: [PATCH 1/2] ipv6: Fix IPsec slowpath fragmentation problem
From: Steffen Klassert <[email protected]>
Date: Tue, 11 Oct 2011 13:43:33 +0200
Quoted 9 lines
ip6_append_data()builds packets based on the mtu fromdst_mtu(rt->dst.path). On IPsec the effective mtu is lower because we need to add the protocol headers and trailers later when we do the IPsec transformations. So after the IPsec transformations the packet might be too big, which leads to a slowpath fragmentation then. This patch fixes this by building the packets based on the lower IPsec mtu fromdst_mtu(&rt->dst)and adapts the exthdr handling to this.Signed-off-by Steffen Klassert <[email protected]>
Applied.
Steffen Klassert
maintainerPATCH 2/2 xfrm6: Don't call icmpv6_send on local error
In reply to Steffen KlassertCalling icmpv6_send() on a local message size error leads to an incorrect update of the path mtu. So use xfrm6_local_rxpmtu() to notify about the pmtu if the IPV6_DONTFRAG socket option is set on an udp or raw socket, according RFC 3542 and use ipv6_local_error() otherwise.
--- net/ipv6/xfrm6_output.c | 56 +++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 54 insertions(+), 2 deletions(-)
net/ipv6/xfrm6_output.c +54 -2
@@ -28,6 +28,43 @@ int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,EXPORT_SYMBOL (xfrm6_find_1stfragopt); +static int xfrm6_local_dontfrag (struct sk_buff *skb) +{ +int proto; +struct sock *sk = skb->sk; + +if (sk) { + proto = sk->sk_protocol; + +if (proto ==IPPROTO_UDP || proto ==IPPROTO_RAW ) +return inet6_sk (sk)->dontfrag; + } + +return 0 ; +} + +static void xfrm6_local_rxpmtu (struct sk_buff *skb,u32 mtu) +{ +struct flowi6 fl6; +struct sock *sk = skb->sk; + + fl6.flowi6_oif = sk->sk_bound_dev_if; +ipv6_addr_copy (&fl6.daddr, &ipv6_hdr (skb)->daddr); + +ipv6_local_rxpmtu (sk, &fl6, mtu); +} + +static void xfrm6_local_error (struct sk_buff *skb,u32 mtu) +{ +struct flowi6 fl6; +struct sock *sk = skb->sk; + + fl6.fl6_dport =inet_sk (sk)->inet_dport; +ipv6_addr_copy (&fl6.daddr, &ipv6_hdr (skb)->daddr); + +ipv6_local_error (sk,EMSGSIZE , &fl6, mtu); +} +static int xfrm6_tunnel_check_size (struct sk_buff *skb) {int mtu, ret =0 ;
@@ -39,7 +76,13 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)if (!skb->local_df && skb->len > mtu) { skb->dev = dst->dev; -icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu); + +if (xfrm6_local_dontfrag (skb)) +xfrm6_local_rxpmtu (skb, mtu); +else if (skb->sk) +xfrm6_local_error (skb, mtu); +else +icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu); ret = -EMSGSIZE ; }
@@ -93,9 +136,18 @@ static int __xfrm6_output(struct sk_buff *skb) {struct dst_entry *dst =skb_dst (skb);struct xfrm_state *x = dst->xfrm; +int mtu =ip6_skb_dst_mtu (skb); + +if (skb->len > mtu &&xfrm6_local_dontfrag (skb)) { +xfrm6_local_rxpmtu (skb, mtu); +return -EMSGSIZE ; + }else if (!skb->local_df && skb->len > mtu && skb->sk) { +xfrm6_local_error (skb, mtu); +return -EMSGSIZE ; + }if ((x && x->props.mode ==XFRM_MODE_TUNNEL ) && - ((skb->len >ip6_skb_dst_mtu (skb) && !skb_is_gso (skb)) || + ((skb->len > mtu && !skb_is_gso (skb)) ||dst_allfrag (skb_dst (skb)))) {return ip6_fragment (skb, x->outer_mode->afinfo->output_finish); }
Signature · 1 line
1.7.0.4
Re: [PATCH 2/2] xfrm6: Don't call icmpv6_send on local error
From: Steffen Klassert <[email protected]>
Date: Tue, 11 Oct 2011 13:44:30 +0200
Quoted 7 lines
Calling
icmpv6_send()on a local message size error leads to an incorrect update of the path mtu. So usexfrm6_local_rxpmtu()to notify about the pmtu if theIPV6_DONTFRAGsocket option is set on an udp or raw socket, according RFC 3542 and useipv6_local_error()otherwise.Signed-off-by Steffen Klassert <[email protected]>
Applied.