kernel: update 3.14 to 3.14.30
[openwrt.git] / target / linux / generic / patches-3.14 / 080-backport_xfrm_crash_fix.patch
1 commit 5596732fa8c14139018ecda8356eabbfb599d830
2 Author: Steffen Klassert <steffen.klassert@secunet.com>
3 Date:   Mon Apr 7 08:08:52 2014 +0200
4
5     xfrm: Fix crash with ipv6 IPsec tunnel and NAT.
6     
7     The ipv6 xfrm output path is not aware that packets can be
8     rerouted by NAT to not use IPsec. We crash in this case
9     because we expect to have a xfrm state at the dst_entry.
10     This crash happens if the ipv6 layer does IPsec and NAT
11     or if we have an interfamily IPsec tunnel with ipv4 NAT.
12     
13     We fix this by checking for a NAT rerouted packet in each
14     address family and dst_output() to the new destination
15     in this case.
16     
17     Reported-by: Martin Pelikan <martin.pelikan@gmail.com>
18     Tested-by: Martin Pelikan <martin.pelikan@gmail.com>
19     Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
20
21 --- a/net/ipv4/xfrm4_output.c
22 +++ b/net/ipv4/xfrm4_output.c
23 @@ -62,10 +62,7 @@ int xfrm4_prepare_output(struct xfrm_sta
24         if (err)
25                 return err;
26  
27 -       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
28 -       IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED;
29 -
30 -       skb->protocol = htons(ETH_P_IP);
31 +       IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
32  
33         return x->outer_mode->output2(x, skb);
34  }
35 @@ -73,27 +70,34 @@ EXPORT_SYMBOL(xfrm4_prepare_output);
36  
37  int xfrm4_output_finish(struct sk_buff *skb)
38  {
39 +       memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
40 +       skb->protocol = htons(ETH_P_IP);
41 +
42 +#ifdef CONFIG_NETFILTER
43 +       IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
44 +#endif
45 +
46 +       return xfrm_output(skb);
47 +}
48 +
49 +static int __xfrm4_output(struct sk_buff *skb)
50 +{
51 +       struct xfrm_state *x = skb_dst(skb)->xfrm;
52 +
53  #ifdef CONFIG_NETFILTER
54 -       if (!skb_dst(skb)->xfrm) {
55 +       if (!x) {
56                 IPCB(skb)->flags |= IPSKB_REROUTED;
57                 return dst_output(skb);
58         }
59 -
60 -       IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
61  #endif
62  
63 -       skb->protocol = htons(ETH_P_IP);
64 -       return xfrm_output(skb);
65 +       return x->outer_mode->afinfo->output_finish(skb);
66  }
67  
68  int xfrm4_output(struct sk_buff *skb)
69  {
70 -       struct dst_entry *dst = skb_dst(skb);
71 -       struct xfrm_state *x = dst->xfrm;
72 -
73         return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb,
74 -                           NULL, dst->dev,
75 -                           x->outer_mode->afinfo->output_finish,
76 +                           NULL, skb_dst(skb)->dev, __xfrm4_output,
77                             !(IPCB(skb)->flags & IPSKB_REROUTED));
78  }
79  
80 --- a/net/ipv6/xfrm6_output.c
81 +++ b/net/ipv6/xfrm6_output.c
82 @@ -114,12 +114,6 @@ int xfrm6_prepare_output(struct xfrm_sta
83         if (err)
84                 return err;
85  
86 -       memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
87 -#ifdef CONFIG_NETFILTER
88 -       IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
89 -#endif
90 -
91 -       skb->protocol = htons(ETH_P_IPV6);
92         skb->local_df = 1;
93  
94         return x->outer_mode->output2(x, skb);
95 @@ -128,11 +122,13 @@ EXPORT_SYMBOL(xfrm6_prepare_output);
96  
97  int xfrm6_output_finish(struct sk_buff *skb)
98  {
99 +       memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
100 +       skb->protocol = htons(ETH_P_IPV6);
101 +
102  #ifdef CONFIG_NETFILTER
103         IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
104  #endif
105  
106 -       skb->protocol = htons(ETH_P_IPV6);
107         return xfrm_output(skb);
108  }
109  
110 @@ -142,6 +138,13 @@ static int __xfrm6_output(struct sk_buff
111         struct xfrm_state *x = dst->xfrm;
112         int mtu;
113  
114 +#ifdef CONFIG_NETFILTER
115 +       if (!x) {
116 +               IP6CB(skb)->flags |= IP6SKB_REROUTED;
117 +               return dst_output(skb);
118 +       }
119 +#endif
120 +
121         if (skb->protocol == htons(ETH_P_IPV6))
122                 mtu = ip6_skb_dst_mtu(skb);
123         else
124 @@ -165,6 +168,7 @@ static int __xfrm6_output(struct sk_buff
125  
126  int xfrm6_output(struct sk_buff *skb)
127  {
128 -       return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL,
129 -                      skb_dst(skb)->dev, __xfrm6_output);
130 +       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb,
131 +                           NULL, skb_dst(skb)->dev, __xfrm6_output,
132 +                           !(IP6CB(skb)->flags & IP6SKB_REROUTED));
133  }