|
17 | 17 | #include <net/netfilter/nf_tables_core.h> |
18 | 18 | #include <net/netfilter/nf_tables.h> |
19 | 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 | 20 | static void nft_payload_eval(const struct nft_expr *expr, |
28 | 21 | struct nft_data data[NFT_REG_MAX + 1], |
29 | 22 | const struct nft_pktinfo *pkt) |
@@ -71,27 +64,9 @@ static int nft_payload_init(const struct nft_ctx *ctx, |
71 | 64 | struct nft_payload *priv = nft_expr_priv(expr); |
72 | 65 | int err; |
73 | 66 |
|
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 | | - |
| 67 | + priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); |
90 | 68 | priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); |
91 | 69 | 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 | 70 |
|
96 | 71 | priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG])); |
97 | 72 | err = nft_validate_output_register(priv->dreg); |
@@ -124,9 +99,49 @@ static const struct nft_expr_ops nft_payload_ops = { |
124 | 99 | .dump = nft_payload_dump, |
125 | 100 | }; |
126 | 101 |
|
| 102 | +const 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 | + |
| 110 | +static const struct nft_expr_ops *nft_payload_select_ops(const struct nlattr * const tb[]) |
| 111 | +{ |
| 112 | + enum nft_payload_bases base; |
| 113 | + unsigned int offset, len; |
| 114 | + |
| 115 | + if (tb[NFTA_PAYLOAD_DREG] == NULL || |
| 116 | + tb[NFTA_PAYLOAD_BASE] == NULL || |
| 117 | + tb[NFTA_PAYLOAD_OFFSET] == NULL || |
| 118 | + tb[NFTA_PAYLOAD_LEN] == NULL) |
| 119 | + return ERR_PTR(-EINVAL); |
| 120 | + |
| 121 | + base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); |
| 122 | + switch (base) { |
| 123 | + case NFT_PAYLOAD_LL_HEADER: |
| 124 | + case NFT_PAYLOAD_NETWORK_HEADER: |
| 125 | + case NFT_PAYLOAD_TRANSPORT_HEADER: |
| 126 | + break; |
| 127 | + default: |
| 128 | + return ERR_PTR(-EOPNOTSUPP); |
| 129 | + } |
| 130 | + |
| 131 | + offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); |
| 132 | + len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); |
| 133 | + if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data)) |
| 134 | + return ERR_PTR(-EINVAL); |
| 135 | + |
| 136 | + if (len <= 4 && IS_ALIGNED(offset, len) && base != NFT_PAYLOAD_LL_HEADER) |
| 137 | + return &nft_payload_fast_ops; |
| 138 | + else |
| 139 | + return &nft_payload_ops; |
| 140 | +} |
| 141 | + |
127 | 142 | static struct nft_expr_type nft_payload_type __read_mostly = { |
128 | 143 | .name = "payload", |
129 | | - .ops = &nft_payload_ops, |
| 144 | + .select_ops = nft_payload_select_ops, |
130 | 145 | .policy = nft_payload_policy, |
131 | 146 | .maxattr = NFTA_PAYLOAD_MAX, |
132 | 147 | .owner = THIS_MODULE, |
|
0 commit comments