]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/netfilter/nft_payload.c
netfilter: nf_tables: expression ops overloading
[karo-tx-linux.git] / net / netfilter / nft_payload.c
1 /*
2  * Copyright (c) 2008-2009 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_core.h>
18 #include <net/netfilter/nf_tables.h>
19
20 struct nft_payload {
21         enum nft_payload_bases  base:8;
22         u8                      offset;
23         u8                      len;
24         enum nft_registers      dreg:8;
25 };
26
27 static void nft_payload_eval(const struct nft_expr *expr,
28                              struct nft_data data[NFT_REG_MAX + 1],
29                              const struct nft_pktinfo *pkt)
30 {
31         const struct nft_payload *priv = nft_expr_priv(expr);
32         const struct sk_buff *skb = pkt->skb;
33         struct nft_data *dest = &data[priv->dreg];
34         int offset;
35
36         switch (priv->base) {
37         case NFT_PAYLOAD_LL_HEADER:
38                 if (!skb_mac_header_was_set(skb))
39                         goto err;
40                 offset = skb_mac_header(skb) - skb->data;
41                 break;
42         case NFT_PAYLOAD_NETWORK_HEADER:
43                 offset = skb_network_offset(skb);
44                 break;
45         case NFT_PAYLOAD_TRANSPORT_HEADER:
46                 offset = skb_transport_offset(skb);
47                 break;
48         default:
49                 BUG();
50         }
51         offset += priv->offset;
52
53         if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0)
54                 goto err;
55         return;
56 err:
57         data[NFT_REG_VERDICT].verdict = NFT_BREAK;
58 }
59
60 static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
61         [NFTA_PAYLOAD_DREG]     = { .type = NLA_U32 },
62         [NFTA_PAYLOAD_BASE]     = { .type = NLA_U32 },
63         [NFTA_PAYLOAD_OFFSET]   = { .type = NLA_U32 },
64         [NFTA_PAYLOAD_LEN]      = { .type = NLA_U32 },
65 };
66
67 static int nft_payload_init(const struct nft_ctx *ctx,
68                             const struct nft_expr *expr,
69                             const struct nlattr * const tb[])
70 {
71         struct nft_payload *priv = nft_expr_priv(expr);
72         int err;
73
74         if (tb[NFTA_PAYLOAD_DREG] == NULL ||
75             tb[NFTA_PAYLOAD_BASE] == NULL ||
76             tb[NFTA_PAYLOAD_OFFSET] == NULL ||
77             tb[NFTA_PAYLOAD_LEN] == NULL)
78                 return -EINVAL;
79
80         priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
81         switch (priv->base) {
82         case NFT_PAYLOAD_LL_HEADER:
83         case NFT_PAYLOAD_NETWORK_HEADER:
84         case NFT_PAYLOAD_TRANSPORT_HEADER:
85                 break;
86         default:
87                 return -EOPNOTSUPP;
88         }
89
90         priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
91         priv->len    = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
92         if (priv->len == 0 ||
93             priv->len > FIELD_SIZEOF(struct nft_data, data))
94                 return -EINVAL;
95
96         priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG]));
97         err = nft_validate_output_register(priv->dreg);
98         if (err < 0)
99                 return err;
100         return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
101 }
102
103 static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)
104 {
105         const struct nft_payload *priv = nft_expr_priv(expr);
106
107         if (nla_put_be32(skb, NFTA_PAYLOAD_DREG, htonl(priv->dreg)) ||
108             nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) ||
109             nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) ||
110             nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len)))
111                 goto nla_put_failure;
112         return 0;
113
114 nla_put_failure:
115         return -1;
116 }
117
118 static struct nft_expr_type nft_payload_type;
119 static const struct nft_expr_ops nft_payload_ops = {
120         .type           = &nft_payload_type,
121         .size           = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
122         .eval           = nft_payload_eval,
123         .init           = nft_payload_init,
124         .dump           = nft_payload_dump,
125 };
126
127 static struct nft_expr_type nft_payload_type __read_mostly = {
128         .name           = "payload",
129         .ops            = &nft_payload_ops,
130         .policy         = nft_payload_policy,
131         .maxattr        = NFTA_PAYLOAD_MAX,
132         .owner          = THIS_MODULE,
133 };
134
135 int __init nft_payload_module_init(void)
136 {
137         return nft_register_expr(&nft_payload_type);
138 }
139
140 void nft_payload_module_exit(void)
141 {
142         nft_unregister_expr(&nft_payload_type);
143 }