blob: d99db6e37fb15d10e97ad4be59d90bb6201517e7 [file] [log] [blame]
Patrick McHardy96518512013-10-14 11:00:02 +02001/*
Patrick McHardyef1f7df2013-10-10 11:41:20 +02002 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
Patrick McHardy96518512013-10-14 11:00:02 +02003 *
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
20struct nft_payload {
21 enum nft_payload_bases base:8;
22 u8 offset;
23 u8 len;
24 enum nft_registers dreg:8;
25};
26
27static 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;
56err:
57 data[NFT_REG_VERDICT].verdict = NFT_BREAK;
58}
59
60static 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
67static 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
103static 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
114nla_put_failure:
115 return -1;
116}
117
Patrick McHardyef1f7df2013-10-10 11:41:20 +0200118static struct nft_expr_type nft_payload_type;
119static const struct nft_expr_ops nft_payload_ops = {
120 .type = &nft_payload_type,
Patrick McHardy96518512013-10-14 11:00:02 +0200121 .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
Patrick McHardy96518512013-10-14 11:00:02 +0200122 .eval = nft_payload_eval,
123 .init = nft_payload_init,
124 .dump = nft_payload_dump,
Patrick McHardyef1f7df2013-10-10 11:41:20 +0200125};
126
127static struct nft_expr_type nft_payload_type __read_mostly = {
128 .name = "payload",
129 .ops = &nft_payload_ops,
Patrick McHardy96518512013-10-14 11:00:02 +0200130 .policy = nft_payload_policy,
131 .maxattr = NFTA_PAYLOAD_MAX,
Patrick McHardyef1f7df2013-10-10 11:41:20 +0200132 .owner = THIS_MODULE,
Patrick McHardy96518512013-10-14 11:00:02 +0200133};
134
135int __init nft_payload_module_init(void)
136{
Patrick McHardyef1f7df2013-10-10 11:41:20 +0200137 return nft_register_expr(&nft_payload_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200138}
139
140void nft_payload_module_exit(void)
141{
Patrick McHardyef1f7df2013-10-10 11:41:20 +0200142 nft_unregister_expr(&nft_payload_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200143}