diff options
| -rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 2 | ||||
| -rw-r--r-- | net/netfilter/nft_ct.c | 164 |
2 files changed, 136 insertions, 30 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 448593c07120..83c985a6170b 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
| @@ -609,12 +609,14 @@ enum nft_ct_keys { | |||
| 609 | * @NFTA_CT_DREG: destination register (NLA_U32) | 609 | * @NFTA_CT_DREG: destination register (NLA_U32) |
| 610 | * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys) | 610 | * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys) |
| 611 | * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8) | 611 | * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8) |
| 612 | * @NFTA_CT_SREG: source register (NLA_U32) | ||
| 612 | */ | 613 | */ |
| 613 | enum nft_ct_attributes { | 614 | enum nft_ct_attributes { |
| 614 | NFTA_CT_UNSPEC, | 615 | NFTA_CT_UNSPEC, |
| 615 | NFTA_CT_DREG, | 616 | NFTA_CT_DREG, |
| 616 | NFTA_CT_KEY, | 617 | NFTA_CT_KEY, |
| 617 | NFTA_CT_DIRECTION, | 618 | NFTA_CT_DIRECTION, |
| 619 | NFTA_CT_SREG, | ||
| 618 | __NFTA_CT_MAX | 620 | __NFTA_CT_MAX |
| 619 | }; | 621 | }; |
| 620 | #define NFTA_CT_MAX (__NFTA_CT_MAX - 1) | 622 | #define NFTA_CT_MAX (__NFTA_CT_MAX - 1) |
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 3727a321c9a7..c7c12858e113 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c | |||
| @@ -18,17 +18,21 @@ | |||
| 18 | #include <net/netfilter/nf_conntrack.h> | 18 | #include <net/netfilter/nf_conntrack.h> |
| 19 | #include <net/netfilter/nf_conntrack_tuple.h> | 19 | #include <net/netfilter/nf_conntrack_tuple.h> |
| 20 | #include <net/netfilter/nf_conntrack_helper.h> | 20 | #include <net/netfilter/nf_conntrack_helper.h> |
| 21 | #include <net/netfilter/nf_conntrack_ecache.h> | ||
| 21 | 22 | ||
| 22 | struct nft_ct { | 23 | struct nft_ct { |
| 23 | enum nft_ct_keys key:8; | 24 | enum nft_ct_keys key:8; |
| 24 | enum ip_conntrack_dir dir:8; | 25 | enum ip_conntrack_dir dir:8; |
| 25 | enum nft_registers dreg:8; | 26 | union{ |
| 27 | enum nft_registers dreg:8; | ||
| 28 | enum nft_registers sreg:8; | ||
| 29 | }; | ||
| 26 | uint8_t family; | 30 | uint8_t family; |
| 27 | }; | 31 | }; |
| 28 | 32 | ||
| 29 | static void nft_ct_eval(const struct nft_expr *expr, | 33 | static void nft_ct_get_eval(const struct nft_expr *expr, |
| 30 | struct nft_data data[NFT_REG_MAX + 1], | 34 | struct nft_data data[NFT_REG_MAX + 1], |
| 31 | const struct nft_pktinfo *pkt) | 35 | const struct nft_pktinfo *pkt) |
| 32 | { | 36 | { |
| 33 | const struct nft_ct *priv = nft_expr_priv(expr); | 37 | const struct nft_ct *priv = nft_expr_priv(expr); |
| 34 | struct nft_data *dest = &data[priv->dreg]; | 38 | struct nft_data *dest = &data[priv->dreg]; |
| @@ -123,10 +127,37 @@ err: | |||
| 123 | data[NFT_REG_VERDICT].verdict = NFT_BREAK; | 127 | data[NFT_REG_VERDICT].verdict = NFT_BREAK; |
| 124 | } | 128 | } |
| 125 | 129 | ||
| 130 | static void nft_ct_set_eval(const struct nft_expr *expr, | ||
| 131 | struct nft_data data[NFT_REG_MAX + 1], | ||
| 132 | const struct nft_pktinfo *pkt) | ||
| 133 | { | ||
| 134 | const struct nft_ct *priv = nft_expr_priv(expr); | ||
| 135 | struct sk_buff *skb = pkt->skb; | ||
| 136 | u32 value = data[priv->sreg].data[0]; | ||
| 137 | enum ip_conntrack_info ctinfo; | ||
| 138 | struct nf_conn *ct; | ||
| 139 | |||
| 140 | ct = nf_ct_get(skb, &ctinfo); | ||
| 141 | if (ct == NULL) | ||
| 142 | return; | ||
| 143 | |||
| 144 | switch (priv->key) { | ||
| 145 | #ifdef CONFIG_NF_CONNTRACK_MARK | ||
| 146 | case NFT_CT_MARK: | ||
| 147 | if (ct->mark != value) { | ||
| 148 | ct->mark = value; | ||
| 149 | nf_conntrack_event_cache(IPCT_MARK, ct); | ||
| 150 | } | ||
| 151 | break; | ||
| 152 | #endif | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 126 | static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { | 156 | static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { |
| 127 | [NFTA_CT_DREG] = { .type = NLA_U32 }, | 157 | [NFTA_CT_DREG] = { .type = NLA_U32 }, |
| 128 | [NFTA_CT_KEY] = { .type = NLA_U32 }, | 158 | [NFTA_CT_KEY] = { .type = NLA_U32 }, |
| 129 | [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, | 159 | [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, |
| 160 | [NFTA_CT_SREG] = { .type = NLA_U32 }, | ||
| 130 | }; | 161 | }; |
| 131 | 162 | ||
| 132 | static int nft_ct_l3proto_try_module_get(uint8_t family) | 163 | static int nft_ct_l3proto_try_module_get(uint8_t family) |
| @@ -162,18 +193,11 @@ static void nft_ct_l3proto_module_put(uint8_t family) | |||
| 162 | nf_ct_l3proto_module_put(family); | 193 | nf_ct_l3proto_module_put(family); |
| 163 | } | 194 | } |
| 164 | 195 | ||
| 165 | static int nft_ct_init(const struct nft_ctx *ctx, | 196 | static int nft_ct_init_validate_get(const struct nft_expr *expr, |
| 166 | const struct nft_expr *expr, | 197 | const struct nlattr * const tb[]) |
| 167 | const struct nlattr * const tb[]) | ||
| 168 | { | 198 | { |
| 169 | struct nft_ct *priv = nft_expr_priv(expr); | 199 | struct nft_ct *priv = nft_expr_priv(expr); |
| 170 | int err; | ||
| 171 | 200 | ||
| 172 | if (tb[NFTA_CT_DREG] == NULL || | ||
| 173 | tb[NFTA_CT_KEY] == NULL) | ||
| 174 | return -EINVAL; | ||
| 175 | |||
| 176 | priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); | ||
| 177 | if (tb[NFTA_CT_DIRECTION] != NULL) { | 201 | if (tb[NFTA_CT_DIRECTION] != NULL) { |
| 178 | priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); | 202 | priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); |
| 179 | switch (priv->dir) { | 203 | switch (priv->dir) { |
| @@ -212,24 +236,62 @@ static int nft_ct_init(const struct nft_ctx *ctx, | |||
| 212 | return -EOPNOTSUPP; | 236 | return -EOPNOTSUPP; |
| 213 | } | 237 | } |
| 214 | 238 | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int nft_ct_init_validate_set(uint32_t key) | ||
| 243 | { | ||
| 244 | switch (key) { | ||
| 245 | case NFT_CT_MARK: | ||
| 246 | break; | ||
| 247 | default: | ||
| 248 | return -EOPNOTSUPP; | ||
| 249 | } | ||
| 250 | |||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | static int nft_ct_init(const struct nft_ctx *ctx, | ||
| 255 | const struct nft_expr *expr, | ||
| 256 | const struct nlattr * const tb[]) | ||
| 257 | { | ||
| 258 | struct nft_ct *priv = nft_expr_priv(expr); | ||
| 259 | int err; | ||
| 260 | |||
| 261 | priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); | ||
| 262 | |||
| 263 | if (tb[NFTA_CT_DREG]) { | ||
| 264 | err = nft_ct_init_validate_get(expr, tb); | ||
| 265 | if (err < 0) | ||
| 266 | return err; | ||
| 267 | |||
| 268 | priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); | ||
| 269 | err = nft_validate_output_register(priv->dreg); | ||
| 270 | if (err < 0) | ||
| 271 | return err; | ||
| 272 | |||
| 273 | err = nft_validate_data_load(ctx, priv->dreg, NULL, | ||
| 274 | NFT_DATA_VALUE); | ||
| 275 | if (err < 0) | ||
| 276 | return err; | ||
| 277 | } else { | ||
| 278 | err = nft_ct_init_validate_set(priv->key); | ||
| 279 | if (err < 0) | ||
| 280 | return err; | ||
| 281 | |||
| 282 | priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG])); | ||
| 283 | err = nft_validate_input_register(priv->sreg); | ||
| 284 | if (err < 0) | ||
| 285 | return err; | ||
| 286 | } | ||
| 287 | |||
| 215 | err = nft_ct_l3proto_try_module_get(ctx->afi->family); | 288 | err = nft_ct_l3proto_try_module_get(ctx->afi->family); |
| 216 | if (err < 0) | 289 | if (err < 0) |
| 217 | return err; | 290 | return err; |
| 218 | priv->family = ctx->afi->family; | ||
| 219 | 291 | ||
| 220 | priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); | 292 | priv->family = ctx->afi->family; |
| 221 | err = nft_validate_output_register(priv->dreg); | ||
| 222 | if (err < 0) | ||
| 223 | goto err1; | ||
| 224 | 293 | ||
| 225 | err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); | ||
| 226 | if (err < 0) | ||
| 227 | goto err1; | ||
| 228 | return 0; | 294 | return 0; |
| 229 | |||
| 230 | err1: | ||
| 231 | nft_ct_l3proto_module_put(ctx->afi->family); | ||
| 232 | return err; | ||
| 233 | } | 295 | } |
| 234 | 296 | ||
| 235 | static void nft_ct_destroy(const struct nft_expr *expr) | 297 | static void nft_ct_destroy(const struct nft_expr *expr) |
| @@ -239,7 +301,7 @@ static void nft_ct_destroy(const struct nft_expr *expr) | |||
| 239 | nft_ct_l3proto_module_put(priv->family); | 301 | nft_ct_l3proto_module_put(priv->family); |
| 240 | } | 302 | } |
| 241 | 303 | ||
| 242 | static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr) | 304 | static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) |
| 243 | { | 305 | { |
| 244 | const struct nft_ct *priv = nft_expr_priv(expr); | 306 | const struct nft_ct *priv = nft_expr_priv(expr); |
| 245 | 307 | ||
| @@ -255,19 +317,61 @@ nla_put_failure: | |||
| 255 | return -1; | 317 | return -1; |
| 256 | } | 318 | } |
| 257 | 319 | ||
| 320 | static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
| 321 | { | ||
| 322 | const struct nft_ct *priv = nft_expr_priv(expr); | ||
| 323 | |||
| 324 | if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg))) | ||
| 325 | goto nla_put_failure; | ||
| 326 | if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) | ||
| 327 | goto nla_put_failure; | ||
| 328 | return 0; | ||
| 329 | |||
| 330 | nla_put_failure: | ||
| 331 | return -1; | ||
| 332 | } | ||
| 333 | |||
| 258 | static struct nft_expr_type nft_ct_type; | 334 | static struct nft_expr_type nft_ct_type; |
| 259 | static const struct nft_expr_ops nft_ct_ops = { | 335 | static const struct nft_expr_ops nft_ct_get_ops = { |
| 260 | .type = &nft_ct_type, | 336 | .type = &nft_ct_type, |
| 261 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | 337 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), |
| 262 | .eval = nft_ct_eval, | 338 | .eval = nft_ct_get_eval, |
| 263 | .init = nft_ct_init, | 339 | .init = nft_ct_init, |
| 264 | .destroy = nft_ct_destroy, | 340 | .destroy = nft_ct_destroy, |
| 265 | .dump = nft_ct_dump, | 341 | .dump = nft_ct_get_dump, |
| 266 | }; | 342 | }; |
| 267 | 343 | ||
| 344 | static const struct nft_expr_ops nft_ct_set_ops = { | ||
| 345 | .type = &nft_ct_type, | ||
| 346 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | ||
| 347 | .eval = nft_ct_set_eval, | ||
| 348 | .init = nft_ct_init, | ||
| 349 | .destroy = nft_ct_destroy, | ||
| 350 | .dump = nft_ct_set_dump, | ||
| 351 | }; | ||
| 352 | |||
| 353 | static const struct nft_expr_ops * | ||
| 354 | nft_ct_select_ops(const struct nft_ctx *ctx, | ||
| 355 | const struct nlattr * const tb[]) | ||
| 356 | { | ||
| 357 | if (tb[NFTA_CT_KEY] == NULL) | ||
| 358 | return ERR_PTR(-EINVAL); | ||
| 359 | |||
| 360 | if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG]) | ||
| 361 | return ERR_PTR(-EINVAL); | ||
| 362 | |||
| 363 | if (tb[NFTA_CT_DREG]) | ||
| 364 | return &nft_ct_get_ops; | ||
| 365 | |||
| 366 | if (tb[NFTA_CT_SREG]) | ||
| 367 | return &nft_ct_set_ops; | ||
| 368 | |||
| 369 | return ERR_PTR(-EINVAL); | ||
| 370 | } | ||
| 371 | |||
| 268 | static struct nft_expr_type nft_ct_type __read_mostly = { | 372 | static struct nft_expr_type nft_ct_type __read_mostly = { |
| 269 | .name = "ct", | 373 | .name = "ct", |
| 270 | .ops = &nft_ct_ops, | 374 | .select_ops = &nft_ct_select_ops, |
| 271 | .policy = nft_ct_policy, | 375 | .policy = nft_ct_policy, |
| 272 | .maxattr = NFTA_CT_MAX, | 376 | .maxattr = NFTA_CT_MAX, |
| 273 | .owner = THIS_MODULE, | 377 | .owner = THIS_MODULE, |
