Merge tag 'linux-watchdog-6.3-rc1' of git://www.linux-watchdog.org/linux-watchdog
[linux-block.git] / net / netfilter / nft_inner.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org>
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/if_vlan.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/netlink.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter/nf_tables.h>
13 #include <net/netfilter/nf_tables_core.h>
14 #include <net/netfilter/nf_tables.h>
15 #include <net/netfilter/nft_meta.h>
16 #include <net/netfilter/nf_tables_offload.h>
17 #include <linux/tcp.h>
18 #include <linux/udp.h>
19 #include <net/gre.h>
20 #include <net/geneve.h>
21 #include <net/ip.h>
22 #include <linux/icmpv6.h>
23 #include <linux/ip.h>
24 #include <linux/ipv6.h>
25
26 static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx);
27
28 /* Same layout as nft_expr but it embeds the private expression data area. */
29 struct __nft_expr {
30         const struct nft_expr_ops       *ops;
31         union {
32                 struct nft_payload      payload;
33                 struct nft_meta         meta;
34         } __attribute__((aligned(__alignof__(u64))));
35 };
36
37 enum {
38         NFT_INNER_EXPR_PAYLOAD,
39         NFT_INNER_EXPR_META,
40 };
41
42 struct nft_inner {
43         u8                      flags;
44         u8                      hdrsize;
45         u8                      type;
46         u8                      expr_type;
47
48         struct __nft_expr       expr;
49 };
50
51 static int nft_inner_parse_l2l3(const struct nft_inner *priv,
52                                 const struct nft_pktinfo *pkt,
53                                 struct nft_inner_tun_ctx *ctx, u32 off)
54 {
55         __be16 llproto, outer_llproto;
56         u32 nhoff, thoff;
57
58         if (priv->flags & NFT_INNER_LL) {
59                 struct vlan_ethhdr *veth, _veth;
60                 struct ethhdr *eth, _eth;
61                 u32 hdrsize;
62
63                 eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth);
64                 if (!eth)
65                         return -1;
66
67                 switch (eth->h_proto) {
68                 case htons(ETH_P_IP):
69                 case htons(ETH_P_IPV6):
70                         llproto = eth->h_proto;
71                         hdrsize = sizeof(_eth);
72                         break;
73                 case htons(ETH_P_8021Q):
74                         veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth);
75                         if (!veth)
76                                 return -1;
77
78                         outer_llproto = veth->h_vlan_encapsulated_proto;
79                         llproto = veth->h_vlan_proto;
80                         hdrsize = sizeof(_veth);
81                         break;
82                 default:
83                         return -1;
84                 }
85
86                 ctx->inner_lloff = off;
87                 ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL;
88                 off += hdrsize;
89         } else {
90                 struct iphdr *iph;
91                 u32 _version;
92
93                 iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version);
94                 if (!iph)
95                         return -1;
96
97                 switch (iph->version) {
98                 case 4:
99                         llproto = htons(ETH_P_IP);
100                         break;
101                 case 6:
102                         llproto = htons(ETH_P_IPV6);
103                         break;
104                 default:
105                         return -1;
106                 }
107         }
108
109         ctx->llproto = llproto;
110         if (llproto == htons(ETH_P_8021Q))
111                 llproto = outer_llproto;
112
113         nhoff = off;
114
115         switch (llproto) {
116         case htons(ETH_P_IP): {
117                 struct iphdr *iph, _iph;
118
119                 iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph);
120                 if (!iph)
121                         return -1;
122
123                 if (iph->ihl < 5 || iph->version != 4)
124                         return -1;
125
126                 ctx->inner_nhoff = nhoff;
127                 ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
128
129                 thoff = nhoff + (iph->ihl * 4);
130                 if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) {
131                         ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
132                         ctx->inner_thoff = thoff;
133                         ctx->l4proto = iph->protocol;
134                 }
135                 }
136                 break;
137         case htons(ETH_P_IPV6): {
138                 struct ipv6hdr *ip6h, _ip6h;
139                 int fh_flags = IP6_FH_F_AUTH;
140                 unsigned short fragoff;
141                 int l4proto;
142
143                 ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h);
144                 if (!ip6h)
145                         return -1;
146
147                 if (ip6h->version != 6)
148                         return -1;
149
150                 ctx->inner_nhoff = nhoff;
151                 ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
152
153                 thoff = nhoff;
154                 l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags);
155                 if (l4proto < 0 || thoff > U16_MAX)
156                         return -1;
157
158                 if (fragoff == 0) {
159                         thoff = nhoff + sizeof(_ip6h);
160                         ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
161                         ctx->inner_thoff = thoff;
162                         ctx->l4proto = l4proto;
163                 }
164                 }
165                 break;
166         default:
167                 return -1;
168         }
169
170         return 0;
171 }
172
173 static int nft_inner_parse_tunhdr(const struct nft_inner *priv,
174                                   const struct nft_pktinfo *pkt,
175                                   struct nft_inner_tun_ctx *ctx, u32 *off)
176 {
177         if (pkt->tprot == IPPROTO_GRE) {
178                 ctx->inner_tunoff = pkt->thoff;
179                 ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
180                 return 0;
181         }
182
183         if (pkt->tprot != IPPROTO_UDP)
184                 return -1;
185
186         ctx->inner_tunoff = *off;
187         ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
188         *off += priv->hdrsize;
189
190         switch (priv->type) {
191         case NFT_INNER_GENEVE: {
192                 struct genevehdr *gnvh, _gnvh;
193
194                 gnvh = skb_header_pointer(pkt->skb, pkt->inneroff,
195                                           sizeof(_gnvh), &_gnvh);
196                 if (!gnvh)
197                         return -1;
198
199                 *off += gnvh->opt_len * 4;
200                 }
201                 break;
202         default:
203                 break;
204         }
205
206         return 0;
207 }
208
209 static int nft_inner_parse(const struct nft_inner *priv,
210                            struct nft_pktinfo *pkt,
211                            struct nft_inner_tun_ctx *tun_ctx)
212 {
213         struct nft_inner_tun_ctx ctx = {};
214         u32 off = pkt->inneroff;
215
216         if (priv->flags & NFT_INNER_HDRSIZE &&
217             nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0)
218                 return -1;
219
220         if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) {
221                 if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0)
222                         return -1;
223         } else if (priv->flags & NFT_INNER_TH) {
224                 ctx.inner_thoff = off;
225                 ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH;
226         }
227
228         *tun_ctx = ctx;
229         tun_ctx->type = priv->type;
230         pkt->flags |= NFT_PKTINFO_INNER_FULL;
231
232         return 0;
233 }
234
235 static bool nft_inner_parse_needed(const struct nft_inner *priv,
236                                    const struct nft_pktinfo *pkt,
237                                    const struct nft_inner_tun_ctx *tun_ctx)
238 {
239         if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
240                 return true;
241
242         if (priv->type != tun_ctx->type)
243                 return true;
244
245         return false;
246 }
247
248 static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
249                            const struct nft_pktinfo *pkt)
250 {
251         struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
252         const struct nft_inner *priv = nft_expr_priv(expr);
253
254         if (nft_payload_inner_offset(pkt) < 0)
255                 goto err;
256
257         if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
258             nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
259                 goto err;
260
261         switch (priv->expr_type) {
262         case NFT_INNER_EXPR_PAYLOAD:
263                 nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
264                 break;
265         case NFT_INNER_EXPR_META:
266                 nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
267                 break;
268         default:
269                 WARN_ON_ONCE(1);
270                 goto err;
271         }
272         return;
273 err:
274         regs->verdict.code = NFT_BREAK;
275 }
276
277 static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = {
278         [NFTA_INNER_NUM]        = { .type = NLA_U32 },
279         [NFTA_INNER_FLAGS]      = { .type = NLA_U32 },
280         [NFTA_INNER_HDRSIZE]    = { .type = NLA_U32 },
281         [NFTA_INNER_TYPE]       = { .type = NLA_U32 },
282         [NFTA_INNER_EXPR]       = { .type = NLA_NESTED },
283 };
284
285 struct nft_expr_info {
286         const struct nft_expr_ops       *ops;
287         const struct nlattr             *attr;
288         struct nlattr                   *tb[NFT_EXPR_MAXATTR + 1];
289 };
290
291 static int nft_inner_init(const struct nft_ctx *ctx,
292                           const struct nft_expr *expr,
293                           const struct nlattr * const tb[])
294 {
295         struct nft_inner *priv = nft_expr_priv(expr);
296         u32 flags, hdrsize, type, num;
297         struct nft_expr_info expr_info;
298         int err;
299
300         if (!tb[NFTA_INNER_FLAGS] ||
301             !tb[NFTA_INNER_HDRSIZE] ||
302             !tb[NFTA_INNER_TYPE] ||
303             !tb[NFTA_INNER_EXPR])
304                 return -EINVAL;
305
306         flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS]));
307         if (flags & ~NFT_INNER_MASK)
308                 return -EOPNOTSUPP;
309
310         num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM]));
311         if (num != 0)
312                 return -EOPNOTSUPP;
313
314         hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE]));
315         type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE]));
316
317         if (type > U8_MAX)
318                 return -EINVAL;
319
320         if (flags & NFT_INNER_HDRSIZE) {
321                 if (hdrsize == 0 || hdrsize > 64)
322                         return -EOPNOTSUPP;
323         }
324
325         priv->flags = flags;
326         priv->hdrsize = hdrsize;
327         priv->type = type;
328
329         err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info);
330         if (err < 0)
331                 return err;
332
333         priv->expr.ops = expr_info.ops;
334
335         if (!strcmp(expr_info.ops->type->name, "payload"))
336                 priv->expr_type = NFT_INNER_EXPR_PAYLOAD;
337         else if (!strcmp(expr_info.ops->type->name, "meta"))
338                 priv->expr_type = NFT_INNER_EXPR_META;
339         else
340                 return -EINVAL;
341
342         err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr,
343                                   (const struct nlattr * const*)expr_info.tb);
344         if (err < 0)
345                 return err;
346
347         return 0;
348 }
349
350 static int nft_inner_dump(struct sk_buff *skb,
351                           const struct nft_expr *expr, bool reset)
352 {
353         const struct nft_inner *priv = nft_expr_priv(expr);
354
355         if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) ||
356             nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) ||
357             nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) ||
358             nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize)))
359                 goto nla_put_failure;
360
361         if (nft_expr_dump(skb, NFTA_INNER_EXPR,
362                           (struct nft_expr *)&priv->expr, reset) < 0)
363                 goto nla_put_failure;
364
365         return 0;
366
367 nla_put_failure:
368         return -1;
369 }
370
371 static const struct nft_expr_ops nft_inner_ops = {
372         .type           = &nft_inner_type,
373         .size           = NFT_EXPR_SIZE(sizeof(struct nft_inner)),
374         .eval           = nft_inner_eval,
375         .init           = nft_inner_init,
376         .dump           = nft_inner_dump,
377 };
378
379 struct nft_expr_type nft_inner_type __read_mostly = {
380         .name           = "inner",
381         .ops            = &nft_inner_ops,
382         .policy         = nft_inner_policy,
383         .maxattr        = NFTA_INNER_MAX,
384         .owner          = THIS_MODULE,
385 };