Fix broken repository link in target/makeccs
[librecmc/librecmc.git] / target / linux / generic / backport-4.9 / 092-netfilter-nf_tables-fix-mismatch-in-big-endian-syste.patch
1 From 0bc01df3df7a88258148518eb9ce7f88c16a6106 Mon Sep 17 00:00:00 2001
2 From: Liping Zhang <zlpnobody@gmail.com>
3 Date: Wed, 8 Mar 2017 22:54:18 +0800
4 Subject: netfilter: nf_tables: fix mismatch in big-endian system
5
6 Currently, there are two different methods to store an u16 integer to
7 the u32 data register. For example:
8   u32 *dest = &regs->data[priv->dreg];
9   1. *dest = 0; *(u16 *) dest = val_u16;
10   2. *dest = val_u16;
11
12 For method 1, the u16 value will be stored like this, either in
13 big-endian or little-endian system:
14   0          15           31
15   +-+-+-+-+-+-+-+-+-+-+-+-+
16   |   Value   |     0     |
17   +-+-+-+-+-+-+-+-+-+-+-+-+
18
19 For method 2, in little-endian system, the u16 value will be the same
20 as listed above. But in big-endian system, the u16 value will be stored
21 like this:
22   0          15           31
23   +-+-+-+-+-+-+-+-+-+-+-+-+
24   |     0     |   Value   |
25   +-+-+-+-+-+-+-+-+-+-+-+-+
26
27 So later we use "memcmp(&regs->data[priv->sreg], data, 2);" to do
28 compare in nft_cmp, nft_lookup expr ..., method 2 will get the wrong
29 result in big-endian system, as 0~15 bits will always be zero.
30
31 For the similar reason, when loading an u16 value from the u32 data
32 register, we should use "*(u16 *) sreg;" instead of "(u16)*sreg;",
33 the 2nd method will get the wrong value in the big-endian system.
34
35 So introduce some wrapper functions to store/load an u8 or u16
36 integer to/from the u32 data register, and use them in the right
37 place.
38
39 Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
40 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
41 ---
42  include/net/netfilter/nf_tables.h   | 29 +++++++++++++++++++++++++
43  net/ipv4/netfilter/nft_masq_ipv4.c  |  8 +++----
44  net/ipv4/netfilter/nft_redir_ipv4.c |  8 +++----
45  net/ipv6/netfilter/nft_masq_ipv6.c  |  8 +++----
46  net/ipv6/netfilter/nft_redir_ipv6.c |  8 +++----
47  net/netfilter/nft_ct.c              | 10 ++++-----
48  net/netfilter/nft_meta.c            | 42 +++++++++++++++++++------------------
49  net/netfilter/nft_nat.c             |  8 +++----
50  8 files changed, 76 insertions(+), 45 deletions(-)
51
52 --- a/include/net/netfilter/nf_tables.h
53 +++ b/include/net/netfilter/nf_tables.h
54 @@ -87,6 +87,35 @@ struct nft_regs {
55         };
56  };
57  
58 +/* Store/load an u16 or u8 integer to/from the u32 data register.
59 + *
60 + * Note, when using concatenations, register allocation happens at 32-bit
61 + * level. So for store instruction, pad the rest part with zero to avoid
62 + * garbage values.
63 + */
64 +
65 +static inline void nft_reg_store16(u32 *dreg, u16 val)
66 +{
67 +       *dreg = 0;
68 +       *(u16 *)dreg = val;
69 +}
70 +
71 +static inline void nft_reg_store8(u32 *dreg, u8 val)
72 +{
73 +       *dreg = 0;
74 +       *(u8 *)dreg = val;
75 +}
76 +
77 +static inline u16 nft_reg_load16(u32 *sreg)
78 +{
79 +       return *(u16 *)sreg;
80 +}
81 +
82 +static inline u8 nft_reg_load8(u32 *sreg)
83 +{
84 +       return *(u8 *)sreg;
85 +}
86 +
87  static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
88                                  unsigned int len)
89  {
90 --- a/net/ipv4/netfilter/nft_masq_ipv4.c
91 +++ b/net/ipv4/netfilter/nft_masq_ipv4.c
92 @@ -26,10 +26,10 @@ static void nft_masq_ipv4_eval(const str
93         memset(&range, 0, sizeof(range));
94         range.flags = priv->flags;
95         if (priv->sreg_proto_min) {
96 -               range.min_proto.all =
97 -                       *(__be16 *)&regs->data[priv->sreg_proto_min];
98 -               range.max_proto.all =
99 -                       *(__be16 *)&regs->data[priv->sreg_proto_max];
100 +               range.min_proto.all = (__force __be16)nft_reg_load16(
101 +                       &regs->data[priv->sreg_proto_min]);
102 +               range.max_proto.all = (__force __be16)nft_reg_load16(
103 +                       &regs->data[priv->sreg_proto_max]);
104         }
105         regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, pkt->hook,
106                                                     &range, pkt->out);
107 --- a/net/ipv4/netfilter/nft_redir_ipv4.c
108 +++ b/net/ipv4/netfilter/nft_redir_ipv4.c
109 @@ -26,10 +26,10 @@ static void nft_redir_ipv4_eval(const st
110  
111         memset(&mr, 0, sizeof(mr));
112         if (priv->sreg_proto_min) {
113 -               mr.range[0].min.all =
114 -                       *(__be16 *)&regs->data[priv->sreg_proto_min];
115 -               mr.range[0].max.all =
116 -                       *(__be16 *)&regs->data[priv->sreg_proto_max];
117 +               mr.range[0].min.all = (__force __be16)nft_reg_load16(
118 +                       &regs->data[priv->sreg_proto_min]);
119 +               mr.range[0].max.all = (__force __be16)nft_reg_load16(
120 +                       &regs->data[priv->sreg_proto_max]);
121                 mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
122         }
123  
124 --- a/net/ipv6/netfilter/nft_masq_ipv6.c
125 +++ b/net/ipv6/netfilter/nft_masq_ipv6.c
126 @@ -27,10 +27,10 @@ static void nft_masq_ipv6_eval(const str
127         memset(&range, 0, sizeof(range));
128         range.flags = priv->flags;
129         if (priv->sreg_proto_min) {
130 -               range.min_proto.all =
131 -                       *(__be16 *)&regs->data[priv->sreg_proto_min];
132 -               range.max_proto.all =
133 -                       *(__be16 *)&regs->data[priv->sreg_proto_max];
134 +               range.min_proto.all = (__force __be16)nft_reg_load16(
135 +                       &regs->data[priv->sreg_proto_min]);
136 +               range.max_proto.all = (__force __be16)nft_reg_load16(
137 +                       &regs->data[priv->sreg_proto_max]);
138         }
139         regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
140  }
141 --- a/net/ipv6/netfilter/nft_redir_ipv6.c
142 +++ b/net/ipv6/netfilter/nft_redir_ipv6.c
143 @@ -26,10 +26,10 @@ static void nft_redir_ipv6_eval(const st
144  
145         memset(&range, 0, sizeof(range));
146         if (priv->sreg_proto_min) {
147 -               range.min_proto.all =
148 -                       *(__be16 *)&regs->data[priv->sreg_proto_min],
149 -               range.max_proto.all =
150 -                       *(__be16 *)&regs->data[priv->sreg_proto_max],
151 +               range.min_proto.all = (__force __be16)nft_reg_load16(
152 +                       &regs->data[priv->sreg_proto_min]);
153 +               range.max_proto.all = (__force __be16)nft_reg_load16(
154 +                       &regs->data[priv->sreg_proto_max]);
155                 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
156         }
157  
158 --- a/net/netfilter/nft_ct.c
159 +++ b/net/netfilter/nft_ct.c
160 @@ -77,7 +77,7 @@ static void nft_ct_get_eval(const struct
161  
162         switch (priv->key) {
163         case NFT_CT_DIRECTION:
164 -               *dest = CTINFO2DIR(ctinfo);
165 +               nft_reg_store8(dest, CTINFO2DIR(ctinfo));
166                 return;
167         case NFT_CT_STATUS:
168                 *dest = ct->status;
169 @@ -129,10 +129,10 @@ static void nft_ct_get_eval(const struct
170                 return;
171         }
172         case NFT_CT_L3PROTOCOL:
173 -               *dest = nf_ct_l3num(ct);
174 +               nft_reg_store8(dest, nf_ct_l3num(ct));
175                 return;
176         case NFT_CT_PROTOCOL:
177 -               *dest = nf_ct_protonum(ct);
178 +               nft_reg_store8(dest, nf_ct_protonum(ct));
179                 return;
180         default:
181                 break;
182 @@ -149,10 +149,10 @@ static void nft_ct_get_eval(const struct
183                        nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
184                 return;
185         case NFT_CT_PROTO_SRC:
186 -               *dest = (__force __u16)tuple->src.u.all;
187 +               nft_reg_store16(dest, (__force u16)tuple->src.u.all);
188                 return;
189         case NFT_CT_PROTO_DST:
190 -               *dest = (__force __u16)tuple->dst.u.all;
191 +               nft_reg_store16(dest, (__force u16)tuple->dst.u.all);
192                 return;
193         default:
194                 break;
195 --- a/net/netfilter/nft_meta.c
196 +++ b/net/netfilter/nft_meta.c
197 @@ -45,16 +45,15 @@ void nft_meta_get_eval(const struct nft_
198                 *dest = skb->len;
199                 break;
200         case NFT_META_PROTOCOL:
201 -               *dest = 0;
202 -               *(__be16 *)dest = skb->protocol;
203 +               nft_reg_store16(dest, (__force u16)skb->protocol);
204                 break;
205         case NFT_META_NFPROTO:
206 -               *dest = pkt->pf;
207 +               nft_reg_store8(dest, pkt->pf);
208                 break;
209         case NFT_META_L4PROTO:
210                 if (!pkt->tprot_set)
211                         goto err;
212 -               *dest = pkt->tprot;
213 +               nft_reg_store8(dest, pkt->tprot);
214                 break;
215         case NFT_META_PRIORITY:
216                 *dest = skb->priority;
217 @@ -85,14 +84,12 @@ void nft_meta_get_eval(const struct nft_
218         case NFT_META_IIFTYPE:
219                 if (in == NULL)
220                         goto err;
221 -               *dest = 0;
222 -               *(u16 *)dest = in->type;
223 +               nft_reg_store16(dest, in->type);
224                 break;
225         case NFT_META_OIFTYPE:
226                 if (out == NULL)
227                         goto err;
228 -               *dest = 0;
229 -               *(u16 *)dest = out->type;
230 +               nft_reg_store16(dest, out->type);
231                 break;
232         case NFT_META_SKUID:
233                 sk = skb_to_full_sk(skb);
234 @@ -142,22 +139,22 @@ void nft_meta_get_eval(const struct nft_
235  #endif
236         case NFT_META_PKTTYPE:
237                 if (skb->pkt_type != PACKET_LOOPBACK) {
238 -                       *dest = skb->pkt_type;
239 +                       nft_reg_store8(dest, skb->pkt_type);
240                         break;
241                 }
242  
243                 switch (pkt->pf) {
244                 case NFPROTO_IPV4:
245                         if (ipv4_is_multicast(ip_hdr(skb)->daddr))
246 -                               *dest = PACKET_MULTICAST;
247 +                               nft_reg_store8(dest, PACKET_MULTICAST);
248                         else
249 -                               *dest = PACKET_BROADCAST;
250 +                               nft_reg_store8(dest, PACKET_BROADCAST);
251                         break;
252                 case NFPROTO_IPV6:
253                         if (ipv6_hdr(skb)->daddr.s6_addr[0] == 0xFF)
254 -                               *dest = PACKET_MULTICAST;
255 +                               nft_reg_store8(dest, PACKET_MULTICAST);
256                         else
257 -                               *dest = PACKET_BROADCAST;
258 +                               nft_reg_store8(dest, PACKET_BROADCAST);
259                         break;
260                 case NFPROTO_NETDEV:
261                         switch (skb->protocol) {
262 @@ -171,14 +168,14 @@ void nft_meta_get_eval(const struct nft_
263                                         goto err;
264  
265                                 if (ipv4_is_multicast(iph->daddr))
266 -                                       *dest = PACKET_MULTICAST;
267 +                                       nft_reg_store8(dest, PACKET_MULTICAST);
268                                 else
269 -                                       *dest = PACKET_BROADCAST;
270 +                                       nft_reg_store8(dest, PACKET_BROADCAST);
271  
272                                 break;
273                         }
274                         case htons(ETH_P_IPV6):
275 -                               *dest = PACKET_MULTICAST;
276 +                               nft_reg_store8(dest, PACKET_MULTICAST);
277                                 break;
278                         default:
279                                 WARN_ON_ONCE(1);
280 @@ -233,7 +230,9 @@ void nft_meta_set_eval(const struct nft_
281  {
282         const struct nft_meta *meta = nft_expr_priv(expr);
283         struct sk_buff *skb = pkt->skb;
284 -       u32 value = regs->data[meta->sreg];
285 +       u32 *sreg = &regs->data[meta->sreg];
286 +       u32 value = *sreg;
287 +       u8 pkt_type;
288  
289         switch (meta->key) {
290         case NFT_META_MARK:
291 @@ -243,9 +242,12 @@ void nft_meta_set_eval(const struct nft_
292                 skb->priority = value;
293                 break;
294         case NFT_META_PKTTYPE:
295 -               if (skb->pkt_type != value &&
296 -                   skb_pkt_type_ok(value) && skb_pkt_type_ok(skb->pkt_type))
297 -                       skb->pkt_type = value;
298 +               pkt_type = nft_reg_load8(sreg);
299 +
300 +               if (skb->pkt_type != pkt_type &&
301 +                   skb_pkt_type_ok(pkt_type) &&
302 +                   skb_pkt_type_ok(skb->pkt_type))
303 +                       skb->pkt_type = pkt_type;
304                 break;
305         case NFT_META_NFTRACE:
306                 skb->nf_trace = !!value;
307 --- a/net/netfilter/nft_nat.c
308 +++ b/net/netfilter/nft_nat.c
309 @@ -65,10 +65,10 @@ static void nft_nat_eval(const struct nf
310         }
311  
312         if (priv->sreg_proto_min) {
313 -               range.min_proto.all =
314 -                       *(__be16 *)&regs->data[priv->sreg_proto_min];
315 -               range.max_proto.all =
316 -                       *(__be16 *)&regs->data[priv->sreg_proto_max];
317 +               range.min_proto.all = (__force __be16)nft_reg_load16(
318 +                       &regs->data[priv->sreg_proto_min]);
319 +               range.max_proto.all = (__force __be16)nft_reg_load16(
320 +                       &regs->data[priv->sreg_proto_max]);
321                 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
322         }
323