~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/nft_exthdr.c

Version: ~ [ linux-6.4-rc3 ] ~ [ linux-6.3.4 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.30 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.113 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.180 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.243 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.283 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.315 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
  3  *
  4  * This program is free software; you can redistribute it and/or modify
  5  * it under the terms of the GNU General Public License version 2 as
  6  * published by the Free Software Foundation.
  7  *
  8  * Development of this code funded by Astaro AG (http://www.astaro.com/)
  9  */
 10 
 11 #include <linux/kernel.h>
 12 #include <linux/init.h>
 13 #include <linux/module.h>
 14 #include <linux/netlink.h>
 15 #include <linux/netfilter.h>
 16 #include <linux/netfilter/nf_tables.h>
 17 #include <net/netfilter/nf_tables.h>
 18 #include <net/tcp.h>
 19 
 20 struct nft_exthdr {
 21         u8                      type;
 22         u8                      offset;
 23         u8                      len;
 24         u8                      op;
 25         enum nft_registers      dreg:8;
 26         u8                      flags;
 27 };
 28 
 29 static unsigned int optlen(const u8 *opt, unsigned int offset)
 30 {
 31         /* Beware zero-length options: make finite progress */
 32         if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
 33                 return 1;
 34         else
 35                 return opt[offset + 1];
 36 }
 37 
 38 static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
 39                                  struct nft_regs *regs,
 40                                  const struct nft_pktinfo *pkt)
 41 {
 42         struct nft_exthdr *priv = nft_expr_priv(expr);
 43         u32 *dest = &regs->data[priv->dreg];
 44         unsigned int offset = 0;
 45         int err;
 46 
 47         err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
 48         if (priv->flags & NFT_EXTHDR_F_PRESENT) {
 49                 *dest = (err >= 0);
 50                 return;
 51         } else if (err < 0) {
 52                 goto err;
 53         }
 54         offset += priv->offset;
 55 
 56         dest[priv->len / NFT_REG32_SIZE] = 0;
 57         if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
 58                 goto err;
 59         return;
 60 err:
 61         regs->verdict.code = NFT_BREAK;
 62 }
 63 
 64 static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
 65                                 struct nft_regs *regs,
 66                                 const struct nft_pktinfo *pkt)
 67 {
 68         u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
 69         struct nft_exthdr *priv = nft_expr_priv(expr);
 70         unsigned int i, optl, tcphdr_len, offset;
 71         u32 *dest = &regs->data[priv->dreg];
 72         struct tcphdr *tcph;
 73         u8 *opt;
 74 
 75         if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
 76                 goto err;
 77 
 78         tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buff);
 79         if (!tcph)
 80                 goto err;
 81 
 82         tcphdr_len = __tcp_hdrlen(tcph);
 83         if (tcphdr_len < sizeof(*tcph))
 84                 goto err;
 85 
 86         tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, tcphdr_len, buff);
 87         if (!tcph)
 88                 goto err;
 89 
 90         opt = (u8 *)tcph;
 91         for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
 92                 optl = optlen(opt, i);
 93 
 94                 if (priv->type != opt[i])
 95                         continue;
 96 
 97                 if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
 98                         goto err;
 99 
100                 offset = i + priv->offset;
101                 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
102                         *dest = 1;
103                 } else {
104                         dest[priv->len / NFT_REG32_SIZE] = 0;
105                         memcpy(dest, opt + offset, priv->len);
106                 }
107 
108                 return;
109         }
110 
111 err:
112         if (priv->flags & NFT_EXTHDR_F_PRESENT)
113                 *dest = 0;
114         else
115                 regs->verdict.code = NFT_BREAK;
116 }
117 
118 static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
119         [NFTA_EXTHDR_DREG]              = { .type = NLA_U32 },
120         [NFTA_EXTHDR_TYPE]              = { .type = NLA_U8 },
121         [NFTA_EXTHDR_OFFSET]            = { .type = NLA_U32 },
122         [NFTA_EXTHDR_LEN]               = { .type = NLA_U32 },
123         [NFTA_EXTHDR_FLAGS]             = { .type = NLA_U32 },
124 };
125 
126 static int nft_exthdr_init(const struct nft_ctx *ctx,
127                            const struct nft_expr *expr,
128                            const struct nlattr * const tb[])
129 {
130         struct nft_exthdr *priv = nft_expr_priv(expr);
131         u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
132         int err;
133 
134         if (!tb[NFTA_EXTHDR_DREG] ||
135             !tb[NFTA_EXTHDR_TYPE] ||
136             !tb[NFTA_EXTHDR_OFFSET] ||
137             !tb[NFTA_EXTHDR_LEN])
138                 return -EINVAL;
139 
140         err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
141         if (err < 0)
142                 return err;
143 
144         err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
145         if (err < 0)
146                 return err;
147 
148         if (tb[NFTA_EXTHDR_FLAGS]) {
149                 err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
150                 if (err < 0)
151                         return err;
152 
153                 if (flags & ~NFT_EXTHDR_F_PRESENT)
154                         return -EINVAL;
155         }
156 
157         if (tb[NFTA_EXTHDR_OP]) {
158                 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
159                 if (err < 0)
160                         return err;
161         }
162 
163         priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
164         priv->offset = offset;
165         priv->len    = len;
166         priv->dreg   = nft_parse_register(tb[NFTA_EXTHDR_DREG]);
167         priv->flags  = flags;
168         priv->op     = op;
169 
170         return nft_validate_register_store(ctx, priv->dreg, NULL,
171                                            NFT_DATA_VALUE, priv->len);
172 }
173 
174 static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
175 {
176         const struct nft_exthdr *priv = nft_expr_priv(expr);
177 
178         if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg))
179                 goto nla_put_failure;
180         if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
181                 goto nla_put_failure;
182         if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
183                 goto nla_put_failure;
184         if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
185                 goto nla_put_failure;
186         if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
187                 goto nla_put_failure;
188         if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
189                 goto nla_put_failure;
190         return 0;
191 
192 nla_put_failure:
193         return -1;
194 }
195 
196 static struct nft_expr_type nft_exthdr_type;
197 static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
198         .type           = &nft_exthdr_type,
199         .size           = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
200         .eval           = nft_exthdr_ipv6_eval,
201         .init           = nft_exthdr_init,
202         .dump           = nft_exthdr_dump,
203 };
204 
205 static const struct nft_expr_ops nft_exthdr_tcp_ops = {
206         .type           = &nft_exthdr_type,
207         .size           = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
208         .eval           = nft_exthdr_tcp_eval,
209         .init           = nft_exthdr_init,
210         .dump           = nft_exthdr_dump,
211 };
212 
213 static const struct nft_expr_ops *
214 nft_exthdr_select_ops(const struct nft_ctx *ctx,
215                       const struct nlattr * const tb[])
216 {
217         u32 op;
218 
219         if (!tb[NFTA_EXTHDR_OP])
220                 return &nft_exthdr_ipv6_ops;
221 
222         op = ntohl(nla_get_u32(tb[NFTA_EXTHDR_OP]));
223         switch (op) {
224         case NFT_EXTHDR_OP_TCPOPT:
225                 return &nft_exthdr_tcp_ops;
226         case NFT_EXTHDR_OP_IPV6:
227                 return &nft_exthdr_ipv6_ops;
228         }
229 
230         return ERR_PTR(-EOPNOTSUPP);
231 }
232 
233 static struct nft_expr_type nft_exthdr_type __read_mostly = {
234         .name           = "exthdr",
235         .select_ops     = nft_exthdr_select_ops,
236         .policy         = nft_exthdr_policy,
237         .maxattr        = NFTA_EXTHDR_MAX,
238         .owner          = THIS_MODULE,
239 };
240 
241 static int __init nft_exthdr_module_init(void)
242 {
243         return nft_register_expr(&nft_exthdr_type);
244 }
245 
246 static void __exit nft_exthdr_module_exit(void)
247 {
248         nft_unregister_expr(&nft_exthdr_type);
249 }
250 
251 module_init(nft_exthdr_module_init);
252 module_exit(nft_exthdr_module_exit);
253 
254 MODULE_LICENSE("GPL");
255 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
256 MODULE_ALIAS_NFT_EXPR("exthdr");
257 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp