Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.15-rc8 161 lines 4.5 kB view raw
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 20static void nft_payload_eval(const struct nft_expr *expr, 21 struct nft_data data[NFT_REG_MAX + 1], 22 const struct nft_pktinfo *pkt) 23{ 24 const struct nft_payload *priv = nft_expr_priv(expr); 25 const struct sk_buff *skb = pkt->skb; 26 struct nft_data *dest = &data[priv->dreg]; 27 int offset; 28 29 switch (priv->base) { 30 case NFT_PAYLOAD_LL_HEADER: 31 if (!skb_mac_header_was_set(skb)) 32 goto err; 33 offset = skb_mac_header(skb) - skb->data; 34 break; 35 case NFT_PAYLOAD_NETWORK_HEADER: 36 offset = skb_network_offset(skb); 37 break; 38 case NFT_PAYLOAD_TRANSPORT_HEADER: 39 offset = pkt->xt.thoff; 40 break; 41 default: 42 BUG(); 43 } 44 offset += priv->offset; 45 46 if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0) 47 goto err; 48 return; 49err: 50 data[NFT_REG_VERDICT].verdict = NFT_BREAK; 51} 52 53static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { 54 [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 }, 55 [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 }, 56 [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 }, 57 [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 }, 58}; 59 60static int nft_payload_init(const struct nft_ctx *ctx, 61 const struct nft_expr *expr, 62 const struct nlattr * const tb[]) 63{ 64 struct nft_payload *priv = nft_expr_priv(expr); 65 int err; 66 67 priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); 68 priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); 69 priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); 70 71 priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG])); 72 err = nft_validate_output_register(priv->dreg); 73 if (err < 0) 74 return err; 75 return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); 76} 77 78static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) 79{ 80 const struct nft_payload *priv = nft_expr_priv(expr); 81 82 if (nla_put_be32(skb, NFTA_PAYLOAD_DREG, htonl(priv->dreg)) || 83 nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) || 84 nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) || 85 nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len))) 86 goto nla_put_failure; 87 return 0; 88 89nla_put_failure: 90 return -1; 91} 92 93static struct nft_expr_type nft_payload_type; 94static const struct nft_expr_ops nft_payload_ops = { 95 .type = &nft_payload_type, 96 .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), 97 .eval = nft_payload_eval, 98 .init = nft_payload_init, 99 .dump = nft_payload_dump, 100}; 101 102const struct nft_expr_ops nft_payload_fast_ops = { 103 .type = &nft_payload_type, 104 .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), 105 .eval = nft_payload_eval, 106 .init = nft_payload_init, 107 .dump = nft_payload_dump, 108}; 109 110static const struct nft_expr_ops * 111nft_payload_select_ops(const struct nft_ctx *ctx, 112 const struct nlattr * const tb[]) 113{ 114 enum nft_payload_bases base; 115 unsigned int offset, len; 116 117 if (tb[NFTA_PAYLOAD_DREG] == NULL || 118 tb[NFTA_PAYLOAD_BASE] == NULL || 119 tb[NFTA_PAYLOAD_OFFSET] == NULL || 120 tb[NFTA_PAYLOAD_LEN] == NULL) 121 return ERR_PTR(-EINVAL); 122 123 base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); 124 switch (base) { 125 case NFT_PAYLOAD_LL_HEADER: 126 case NFT_PAYLOAD_NETWORK_HEADER: 127 case NFT_PAYLOAD_TRANSPORT_HEADER: 128 break; 129 default: 130 return ERR_PTR(-EOPNOTSUPP); 131 } 132 133 offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); 134 len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); 135 if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data)) 136 return ERR_PTR(-EINVAL); 137 138 if (len <= 4 && is_power_of_2(len) && IS_ALIGNED(offset, len) && 139 base != NFT_PAYLOAD_LL_HEADER) 140 return &nft_payload_fast_ops; 141 else 142 return &nft_payload_ops; 143} 144 145static struct nft_expr_type nft_payload_type __read_mostly = { 146 .name = "payload", 147 .select_ops = nft_payload_select_ops, 148 .policy = nft_payload_policy, 149 .maxattr = NFTA_PAYLOAD_MAX, 150 .owner = THIS_MODULE, 151}; 152 153int __init nft_payload_module_init(void) 154{ 155 return nft_register_expr(&nft_payload_type); 156} 157 158void nft_payload_module_exit(void) 159{ 160 nft_unregister_expr(&nft_payload_type); 161}