kernel: preserve oif of IPv6 link scope packets
[openwrt/staging/dedeckeh.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