From: Greg Kroah-Hartman Date: Sun, 3 Jun 2012 12:08:17 +0000 (-0700) Subject: 3.4-stable patches X-Git-Tag: v3.0.34~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=54452338b018acb424a15ab7b6ba63bd9708fc5d;p=thirdparty%2Fkernel%2Fstable-queue.git 3.4-stable patches added patches: asix-allow-full-size-8021q-frames-to-be-received.patch ipv4-fix-the-rcu-race-between-free_fib_info-and-ip_route_output_slow.patch ipv6-fix-incorrect-ipsec-fragment.patch l2tp-fix-oops-in-l2tp-ip-sockets-for-connect-af_unspec-case.patch skb-avoid-unnecessary-reallocations-in-__skb_cow.patch xfrm-take-net-hdr-len-into-account-for-esp-payload-size-calculation.patch --- diff --git a/queue-3.4/asix-allow-full-size-8021q-frames-to-be-received.patch b/queue-3.4/asix-allow-full-size-8021q-frames-to-be-received.patch new file mode 100644 index 00000000000..4d81a1e5437 --- /dev/null +++ b/queue-3.4/asix-allow-full-size-8021q-frames-to-be-received.patch @@ -0,0 +1,46 @@ +From 79fb330c51db3b91f5e4cfbd4a305e4a481785a2 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Mon, 28 May 2012 22:31:41 +0000 +Subject: asix: allow full size 8021Q frames to be received + + +From: Eric Dumazet + +[ Upstream commit 9dae31009b1a00d926c6fe032d5a88099620adc3 ] + +asix driver drops 8021Q full size frames because it doesn't take into +account VLAN header size. + +Tested on AX88772 adapter. + +Signed-off-by: Eric Dumazet +CC: Greg Kroah-Hartman +CC: Allan Chou +CC: Trond Wuellner +CC: Grant Grundler +CC: Paul Stewart +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/usb/asix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/usb/asix.c ++++ b/drivers/net/usb/asix.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + + #define DRIVER_VERSION "22-Dec-2011" + #define DRIVER_NAME "asix" +@@ -321,7 +322,7 @@ static int asix_rx_fixup(struct usbnet * + return 0; + } + +- if ((size > dev->net->mtu + ETH_HLEN) || ++ if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || + (size + offset > skb->len)) { + netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", + size); diff --git a/queue-3.4/ipv4-fix-the-rcu-race-between-free_fib_info-and-ip_route_output_slow.patch b/queue-3.4/ipv4-fix-the-rcu-race-between-free_fib_info-and-ip_route_output_slow.patch new file mode 100644 index 00000000000..4a17c54b001 --- /dev/null +++ b/queue-3.4/ipv4-fix-the-rcu-race-between-free_fib_info-and-ip_route_output_slow.patch @@ -0,0 +1,89 @@ +From cbb5c7c2b1bd52c8929e78b2b0b532e03e6c4e5d Mon Sep 17 00:00:00 2001 +From: Yanmin Zhang +Date: Wed, 23 May 2012 15:39:45 +0000 +Subject: ipv4: fix the rcu race between free_fib_info and ip_route_output_slow + + +From: Yanmin Zhang + +[ Upstream commit e49cc0da7283088c5e03d475ffe2fdcb24a6d5b1 ] + +We hit a kernel OOPS. + +<3>[23898.789643] BUG: sleeping function called from invalid context at +/data/buildbot/workdir/ics/hardware/intel/linux-2.6/arch/x86/mm/fault.c:1103 +<3>[23898.862215] in_atomic(): 0, irqs_disabled(): 0, pid: 10526, name: +Thread-6683 +<4>[23898.967805] HSU serial 0000:00:05.1: 0000:00:05.2:HSU serial prevented me +to suspend... +<4>[23899.258526] Pid: 10526, comm: Thread-6683 Tainted: G W +3.0.8-137685-ge7742f9 #1 +<4>[23899.357404] HSU serial 0000:00:05.1: 0000:00:05.2:HSU serial prevented me +to suspend... +<4>[23899.904225] Call Trace: +<4>[23899.989209] [] ? pgtable_bad+0x130/0x130 +<4>[23900.000416] [] __might_sleep+0x10a/0x110 +<4>[23900.007357] [] do_page_fault+0xd1/0x3c0 +<4>[23900.013764] [] ? restore_all+0xf/0xf +<4>[23900.024024] [] ? napi_complete+0x8b/0x690 +<4>[23900.029297] [] ? pgtable_bad+0x130/0x130 +<4>[23900.123739] [] ? pgtable_bad+0x130/0x130 +<4>[23900.128955] [] error_code+0x5f/0x64 +<4>[23900.133466] [] ? pgtable_bad+0x130/0x130 +<4>[23900.138450] [] ? __ip_route_output_key+0x698/0x7c0 +<4>[23900.144312] [] ? __ip_route_output_key+0x38d/0x7c0 +<4>[23900.150730] [] ip_route_output_flow+0x1f/0x60 +<4>[23900.156261] [] ip4_datagram_connect+0x188/0x2b0 +<4>[23900.161960] [] ? _raw_spin_unlock_bh+0x1f/0x30 +<4>[23900.167834] [] inet_dgram_connect+0x36/0x80 +<4>[23900.173224] [] ? _copy_from_user+0x48/0x140 +<4>[23900.178817] [] sys_connect+0x9a/0xd0 +<4>[23900.183538] [] ? alloc_file+0xdc/0x240 +<4>[23900.189111] [] ? sub_preempt_count+0x3d/0x50 + +Function free_fib_info resets nexthop_nh->nh_dev to NULL before releasing +fi. Other cpu might be accessing fi. Fixing it by delaying the releasing. + +With the patch, we ran MTBF testing on Android mobile for 12 hours +and didn't trigger the issue. + +Thank Eric for very detailed review/checking the issue. + +Signed-off-by: Yanmin Zhang +Signed-off-by: Kun Jiang +Acked-by: Eric Dumazet +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + net/ipv4/fib_semantics.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -145,6 +145,12 @@ static void free_fib_info_rcu(struct rcu + { + struct fib_info *fi = container_of(head, struct fib_info, rcu); + ++ change_nexthops(fi) { ++ if (nexthop_nh->nh_dev) ++ dev_put(nexthop_nh->nh_dev); ++ } endfor_nexthops(fi); ++ ++ release_net(fi->fib_net); + if (fi->fib_metrics != (u32 *) dst_default_metrics) + kfree(fi->fib_metrics); + kfree(fi); +@@ -156,13 +162,7 @@ void free_fib_info(struct fib_info *fi) + pr_warn("Freeing alive fib_info %p\n", fi); + return; + } +- change_nexthops(fi) { +- if (nexthop_nh->nh_dev) +- dev_put(nexthop_nh->nh_dev); +- nexthop_nh->nh_dev = NULL; +- } endfor_nexthops(fi); + fib_info_cnt--; +- release_net(fi->fib_net); + call_rcu(&fi->rcu, free_fib_info_rcu); + } + diff --git a/queue-3.4/ipv6-fix-incorrect-ipsec-fragment.patch b/queue-3.4/ipv6-fix-incorrect-ipsec-fragment.patch new file mode 100644 index 00000000000..4e995332cd3 --- /dev/null +++ b/queue-3.4/ipv6-fix-incorrect-ipsec-fragment.patch @@ -0,0 +1,175 @@ +From f5d52863e05bbd77c59a7c0736a5d7f7bf3ff24c Mon Sep 17 00:00:00 2001 +From: Gao feng +Date: Sat, 26 May 2012 01:30:53 +0000 +Subject: ipv6: fix incorrect ipsec fragment + + +From: Gao feng + +[ Upstream commit 0c1833797a5a6ec23ea9261d979aa18078720b74 ] + +Since commit ad0081e43a +"ipv6: Fragment locally generated tunnel-mode IPSec6 packets as needed" +the fragment of packets is incorrect. +because tunnel mode needs IPsec headers and trailer for all fragments, +while on transport mode it is sufficient to add the headers to the +first fragment and the trailer to the last. + +so modify mtu and maxfraglen base on ipsec mode and if fragment is first +or last. + +with my test,it work well(every fragment's size is the mtu) +and does not trigger slow fragment path. + +Changes from v1: + though optimization, mtu_prev and maxfraglen_prev can be delete. + replace xfrm mode codes with dst_entry's new frag DST_XFRM_TUNNEL. + add fuction ip6_append_data_mtu to make codes clearer. + +Signed-off-by: Gao feng +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + include/net/dst.h | 1 + net/ipv6/ip6_output.c | 68 ++++++++++++++++++++++++++++++++++++------------- + net/xfrm/xfrm_policy.c | 3 ++ + 3 files changed, 54 insertions(+), 18 deletions(-) + +--- a/include/net/dst.h ++++ b/include/net/dst.h +@@ -60,6 +60,7 @@ struct dst_entry { + #define DST_NOCOUNT 0x0020 + #define DST_NOPEER 0x0040 + #define DST_FAKE_RTABLE 0x0080 ++#define DST_XFRM_TUNNEL 0x0100 + + short error; + short obsolete; +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1181,6 +1181,29 @@ static inline struct ipv6_rt_hdr *ip6_rt + return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; + } + ++static void ip6_append_data_mtu(int *mtu, ++ int *maxfraglen, ++ unsigned int fragheaderlen, ++ struct sk_buff *skb, ++ struct rt6_info *rt) ++{ ++ if (!(rt->dst.flags & DST_XFRM_TUNNEL)) { ++ if (skb == NULL) { ++ /* first fragment, reserve header_len */ ++ *mtu = *mtu - rt->dst.header_len; ++ ++ } else { ++ /* ++ * this fragment is not first, the headers ++ * space is regarded as data space. ++ */ ++ *mtu = dst_mtu(rt->dst.path); ++ } ++ *maxfraglen = ((*mtu - fragheaderlen) & ~7) ++ + fragheaderlen - sizeof(struct frag_hdr); ++ } ++} ++ + int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, + int offset, int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, +@@ -1190,7 +1213,7 @@ int ip6_append_data(struct sock *sk, int + struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); + struct inet_cork *cork; +- struct sk_buff *skb; ++ struct sk_buff *skb, *skb_prev = NULL; + unsigned int maxfraglen, fragheaderlen; + int exthdrlen; + int dst_exthdrlen; +@@ -1248,8 +1271,12 @@ int ip6_append_data(struct sock *sk, int + inet->cork.fl.u.ip6 = *fl6; + np->cork.hop_limit = hlimit; + np->cork.tclass = tclass; +- mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? +- rt->dst.dev->mtu : dst_mtu(&rt->dst); ++ if (rt->dst.flags & DST_XFRM_TUNNEL) ++ mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? ++ rt->dst.dev->mtu : dst_mtu(&rt->dst); ++ else ++ mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ? ++ rt->dst.dev->mtu : dst_mtu(rt->dst.path); + if (np->frag_size < mtu) { + if (np->frag_size) + mtu = np->frag_size; +@@ -1345,25 +1372,27 @@ int ip6_append_data(struct sock *sk, int + unsigned int fraglen; + unsigned int fraggap; + unsigned int alloclen; +- struct sk_buff *skb_prev; + alloc_new_skb: +- skb_prev = skb; +- + /* There's no room in the current skb */ +- if (skb_prev) +- fraggap = skb_prev->len - maxfraglen; ++ if (skb) ++ fraggap = skb->len - maxfraglen; + else + fraggap = 0; ++ /* update mtu and maxfraglen if necessary */ ++ if (skb == NULL || skb_prev == NULL) ++ ip6_append_data_mtu(&mtu, &maxfraglen, ++ fragheaderlen, skb, rt); ++ ++ skb_prev = skb; + + /* + * If remaining data exceeds the mtu, + * we know we need more fragment(s). + */ + datalen = length + fraggap; +- if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) +- datalen = maxfraglen - fragheaderlen; + +- fraglen = datalen + fragheaderlen; ++ if (datalen > (cork->length <= mtu && !(cork->flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) ++ datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len; + if ((flags & MSG_MORE) && + !(rt->dst.dev->features&NETIF_F_SG)) + alloclen = mtu; +@@ -1372,13 +1401,16 @@ alloc_new_skb: + + alloclen += dst_exthdrlen; + +- /* +- * The last fragment gets additional space at tail. +- * Note: we overallocate on fragments with MSG_MODE +- * because we have no idea if we're the last one. +- */ +- if (datalen == length + fraggap) +- alloclen += rt->dst.trailer_len; ++ if (datalen != length + fraggap) { ++ /* ++ * this is not the last fragment, the trailer ++ * space is regarded as data space. ++ */ ++ datalen += rt->dst.trailer_len; ++ } ++ ++ alloclen += rt->dst.trailer_len; ++ fraglen = datalen + fragheaderlen; + + /* + * We just reserve space for fragment header. +--- a/net/xfrm/xfrm_policy.c ++++ b/net/xfrm/xfrm_policy.c +@@ -1919,6 +1919,9 @@ no_transform: + } + ok: + xfrm_pols_put(pols, drop_pols); ++ if (dst && dst->xfrm && ++ dst->xfrm->props.mode == XFRM_MODE_TUNNEL) ++ dst->flags |= DST_XFRM_TUNNEL; + return dst; + + nopol: diff --git a/queue-3.4/l2tp-fix-oops-in-l2tp-ip-sockets-for-connect-af_unspec-case.patch b/queue-3.4/l2tp-fix-oops-in-l2tp-ip-sockets-for-connect-af_unspec-case.patch new file mode 100644 index 00000000000..79bb121190d --- /dev/null +++ b/queue-3.4/l2tp-fix-oops-in-l2tp-ip-sockets-for-connect-af_unspec-case.patch @@ -0,0 +1,111 @@ +From 2f456e48e2ebd7455a5deb6cad8cc7da917dc028 Mon Sep 17 00:00:00 2001 +From: James Chapman +Date: Tue, 29 May 2012 23:13:23 +0000 +Subject: l2tp: fix oops in L2TP IP sockets for connect() AF_UNSPEC case + + +From: James Chapman + +[ Upstream commit c51ce49735c183ef2592db70f918ee698716276b ] + +An application may call connect() to disconnect a socket using an +address with family AF_UNSPEC. The L2TP IP sockets were not handling +this case when the socket is not bound and an attempt to connect() +using AF_UNSPEC in such cases would result in an oops. This patch +addresses the problem by protecting the sk_prot->disconnect() call +against trying to unhash the socket before it is bound. + +The patch also adds more checks that the sockaddr supplied to bind() +and connect() calls is valid. + + RIP: 0010:[] [] inet_unhash+0x50/0xd0 + RSP: 0018:ffff88001989be28 EFLAGS: 00010293 + Stack: + ffff8800407a8000 0000000000000000 ffff88001989be78 ffffffff82e3a249 + ffffffff82e3a050 ffff88001989bec8 ffff88001989be88 ffff8800407a8000 + 0000000000000010 ffff88001989bec8 ffff88001989bea8 ffffffff82e42639 + Call Trace: + [] udp_disconnect+0x1f9/0x290 + [] inet_dgram_connect+0x29/0x80 + [] sys_connect+0x9c/0x100 + +Reported-by: Sasha Levin +Signed-off-by: James Chapman +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_ip.c | 30 ++++++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 6 deletions(-) + +--- a/net/l2tp/l2tp_ip.c ++++ b/net/l2tp/l2tp_ip.c +@@ -251,9 +251,16 @@ static int l2tp_ip_bind(struct sock *sk, + { + struct inet_sock *inet = inet_sk(sk); + struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr; +- int ret = -EINVAL; ++ int ret; + int chk_addr_ret; + ++ if (!sock_flag(sk, SOCK_ZAPPED)) ++ return -EINVAL; ++ if (addr_len < sizeof(struct sockaddr_l2tpip)) ++ return -EINVAL; ++ if (addr->l2tp_family != AF_INET) ++ return -EINVAL; ++ + ret = -EADDRINUSE; + read_lock_bh(&l2tp_ip_lock); + if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id)) +@@ -284,6 +291,8 @@ static int l2tp_ip_bind(struct sock *sk, + sk_del_node_init(sk); + write_unlock_bh(&l2tp_ip_lock); + ret = 0; ++ sock_reset_flag(sk, SOCK_ZAPPED); ++ + out: + release_sock(sk); + +@@ -304,13 +313,14 @@ static int l2tp_ip_connect(struct sock * + __be32 saddr; + int oif, rc; + +- rc = -EINVAL; ++ if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */ ++ return -EINVAL; ++ + if (addr_len < sizeof(*lsa)) +- goto out; ++ return -EINVAL; + +- rc = -EAFNOSUPPORT; + if (lsa->l2tp_family != AF_INET) +- goto out; ++ return -EAFNOSUPPORT; + + lock_sock(sk); + +@@ -364,6 +374,14 @@ out: + return rc; + } + ++static int l2tp_ip_disconnect(struct sock *sk, int flags) ++{ ++ if (sock_flag(sk, SOCK_ZAPPED)) ++ return 0; ++ ++ return udp_disconnect(sk, flags); ++} ++ + static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) + { +@@ -599,7 +617,7 @@ static struct proto l2tp_ip_prot = { + .close = l2tp_ip_close, + .bind = l2tp_ip_bind, + .connect = l2tp_ip_connect, +- .disconnect = udp_disconnect, ++ .disconnect = l2tp_ip_disconnect, + .ioctl = udp_ioctl, + .destroy = l2tp_ip_destroy_sock, + .setsockopt = ip_setsockopt, diff --git a/queue-3.4/series b/queue-3.4/series index 6d92593e7ab..56f12c68ce1 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -50,3 +50,9 @@ drm-radeon-properly-program-gart-on-rv740-juniper-cypress-barts-hemlock.patch drm-radeon-fix-hd6790-hd6570-backend-programming.patch drm-ttm-fix-spinlock-imbalance.patch drm-vmwgfx-fix-nasty-write-past-alloced-memory-area.patch +asix-allow-full-size-8021q-frames-to-be-received.patch +ipv4-fix-the-rcu-race-between-free_fib_info-and-ip_route_output_slow.patch +ipv6-fix-incorrect-ipsec-fragment.patch +l2tp-fix-oops-in-l2tp-ip-sockets-for-connect-af_unspec-case.patch +skb-avoid-unnecessary-reallocations-in-__skb_cow.patch +xfrm-take-net-hdr-len-into-account-for-esp-payload-size-calculation.patch diff --git a/queue-3.4/skb-avoid-unnecessary-reallocations-in-__skb_cow.patch b/queue-3.4/skb-avoid-unnecessary-reallocations-in-__skb_cow.patch new file mode 100644 index 00000000000..f1fefc2da7e --- /dev/null +++ b/queue-3.4/skb-avoid-unnecessary-reallocations-in-__skb_cow.patch @@ -0,0 +1,40 @@ +From 64305df8b10a0ea32cbef6a880c0b8911cca6cb8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Tue, 29 May 2012 03:35:08 +0000 +Subject: skb: avoid unnecessary reallocations in __skb_cow + + +From: Felix Fietkau + +[ Upstream commit 617c8c11236716dcbda877e764b7bf37c6fd8063 ] + +At the beginning of __skb_cow, headroom gets set to a minimum of +NET_SKB_PAD. This causes unnecessary reallocations if the buffer was not +cloned and the headroom is just below NET_SKB_PAD, but still more than the +amount requested by the caller. +This was showing up frequently in my tests on VLAN tx, where +vlan_insert_tag calls skb_cow_head(skb, VLAN_HLEN). + +Locally generated packets should have enough headroom, and for forward +paths, we already have NET_SKB_PAD bytes of headroom, so we don't need to +add any extra space here. + +Signed-off-by: Felix Fietkau +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/skbuff.h | 2 -- + 1 file changed, 2 deletions(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -1881,8 +1881,6 @@ static inline int __skb_cow(struct sk_bu + { + int delta = 0; + +- if (headroom < NET_SKB_PAD) +- headroom = NET_SKB_PAD; + if (headroom > skb_headroom(skb)) + delta = headroom - skb_headroom(skb); + diff --git a/queue-3.4/xfrm-take-net-hdr-len-into-account-for-esp-payload-size-calculation.patch b/queue-3.4/xfrm-take-net-hdr-len-into-account-for-esp-payload-size-calculation.patch new file mode 100644 index 00000000000..e990c150912 --- /dev/null +++ b/queue-3.4/xfrm-take-net-hdr-len-into-account-for-esp-payload-size-calculation.patch @@ -0,0 +1,95 @@ +From 23e1aa6f7e9515763ea416c4aa6acb9eb138936d Mon Sep 17 00:00:00 2001 +From: Benjamin Poirier +Date: Thu, 24 May 2012 11:32:38 +0000 +Subject: xfrm: take net hdr len into account for esp payload size calculation + + +From: Benjamin Poirier + +[ Upstream commit 91657eafb64b4cb53ec3a2fbc4afc3497f735788 ] + +Corrects the function that determines the esp payload size. The calculations +done in esp{4,6}_get_mtu() lead to overlength frames in transport mode for +certain mtu values and suboptimal frames for others. + +According to what is done, mainly in esp{,6}_output() and tcp_mtu_to_mss(), +net_header_len must be taken into account before doing the alignment +calculation. + +Signed-off-by: Benjamin Poirier +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + net/ipv4/esp4.c | 24 +++++++++--------------- + net/ipv6/esp6.c | 18 +++++++----------- + 2 files changed, 16 insertions(+), 26 deletions(-) + +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -459,28 +459,22 @@ static u32 esp4_get_mtu(struct xfrm_stat + struct esp_data *esp = x->data; + u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4); + u32 align = max_t(u32, blksize, esp->padlen); +- u32 rem; +- +- mtu -= x->props.header_len + crypto_aead_authsize(esp->aead); +- rem = mtu & (align - 1); +- mtu &= ~(align - 1); ++ unsigned int net_adj; + + switch (x->props.mode) { +- case XFRM_MODE_TUNNEL: +- break; +- default: + case XFRM_MODE_TRANSPORT: +- /* The worst case */ +- mtu -= blksize - 4; +- mtu += min_t(u32, blksize - 4, rem); +- break; + case XFRM_MODE_BEET: +- /* The worst case. */ +- mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem); ++ net_adj = sizeof(struct iphdr); ++ break; ++ case XFRM_MODE_TUNNEL: ++ net_adj = 0; + break; ++ default: ++ BUG(); + } + +- return mtu - 2; ++ return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - ++ net_adj) & ~(align - 1)) + (net_adj - 2); + } + + static void esp4_err(struct sk_buff *skb, u32 info) +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -411,19 +411,15 @@ static u32 esp6_get_mtu(struct xfrm_stat + struct esp_data *esp = x->data; + u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4); + u32 align = max_t(u32, blksize, esp->padlen); +- u32 rem; ++ unsigned int net_adj; + +- mtu -= x->props.header_len + crypto_aead_authsize(esp->aead); +- rem = mtu & (align - 1); +- mtu &= ~(align - 1); ++ if (x->props.mode != XFRM_MODE_TUNNEL) ++ net_adj = sizeof(struct ipv6hdr); ++ else ++ net_adj = 0; + +- if (x->props.mode != XFRM_MODE_TUNNEL) { +- u32 padsize = ((blksize - 1) & 7) + 1; +- mtu -= blksize - padsize; +- mtu += min_t(u32, blksize - padsize, rem); +- } +- +- return mtu - 2; ++ return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - ++ net_adj) & ~(align - 1)) + (net_adj - 2); + } + + static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,