Pablo Neira Ayuso says: ==================== Netfilter/IPVS updates for net-next This small patchset contains three accumulated Netfilter/IPVS updates, they are: 1) Refactorize common NAT code by encapsulating it into a helper function, similarly to what we do in other conntrack extensions, from Florian Westphal. 2) A minor format string mismatch fix for IPVS, from Masanari Iida. 3) Add quota support to the netfilter accounting infrastructure, now you can add quotas to accounting objects via the nfnetlink interface and use them from iptables. You can also listen to quota notifications from userspace. This enhancement from Mathieu Poirier. ====================
Unified · Split
| @@ -3,11 +3,17 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | #include <uapi/linux/netfilter/nfnetlink_acct.h> |
| 5 | 5 | |
| 6 | +enum { | |
| 7 | + NFACCT_NO_QUOTA = -1, | |
| 8 | + NFACCT_UNDERQUOTA, | |
| 9 | + NFACCT_OVERQUOTA, | |
| 10 | +}; | |
| 6 | 11 | |
| 7 | 12 | struct nf_acct; |
| 8 | 13 | |
| 9 | 14 | struct nf_acct *nfnl_acct_find_get(const char *filter_name); |
| 10 | 15 | void nfnl_acct_put(struct nf_acct *acct); |
| 11 | 16 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); |
| 12 | − | |
| 17 | +extern int nfnl_acct_overquota(const struct sk_buff *skb, | |
| 18 | + struct nf_acct *nfacct); | |
| 13 | 19 | #endif /* _NFNL_ACCT_H */ |
| @@ -48,6 +48,8 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct, · 47 unmodified lines | ||
| 48 | 48 | extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct, |
| 49 | 49 | unsigned int hooknum); |
| 50 | 50 | |
| 51 | +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct); | |
| 52 | + | |
| 51 | 53 | /* Is this tuple already taken? (not by us)*/ |
| 52 | 54 | int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, |
| 53 | 55 | const struct nf_conn *ignored_conntrack); |
| @@ -20,6 +20,8 @@ enum nfnetlink_groups { · 19 unmodified lines | ||
| 20 | 20 | #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY |
| 21 | 21 | NFNLGRP_NFTABLES, |
| 22 | 22 | #define NFNLGRP_NFTABLES NFNLGRP_NFTABLES |
| 23 | + NFNLGRP_ACCT_QUOTA, | |
| 24 | +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA | |
| 23 | 25 | __NFNLGRP_MAX, |
| 24 | 26 | }; |
| 25 | 27 | #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) |
| @@ -10,15 +10,24 @@ enum nfnl_acct_msg_types { · 9 unmodified lines | ||
| 10 | 10 | NFNL_MSG_ACCT_GET, |
| 11 | 11 | NFNL_MSG_ACCT_GET_CTRZERO, |
| 12 | 12 | NFNL_MSG_ACCT_DEL, |
| 13 | + NFNL_MSG_ACCT_OVERQUOTA, | |
| 13 | 14 | NFNL_MSG_ACCT_MAX |
| 14 | 15 | }; |
| 15 | 16 | |
| 17 | +enum nfnl_acct_flags { | |
| 18 | + NFACCT_F_QUOTA_PKTS = (1 << 0), | |
| 19 | + NFACCT_F_QUOTA_BYTES = (1 << 1), | |
| 20 | + NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */ | |
| 21 | +}; | |
| 22 | + | |
| 16 | 23 | enum nfnl_acct_type { |
| 17 | 24 | NFACCT_UNSPEC, |
| 18 | 25 | NFACCT_NAME, |
| 19 | 26 | NFACCT_PKTS, |
| 20 | 27 | NFACCT_BYTES, |
| 21 | 28 | NFACCT_USE, |
| 29 | + NFACCT_FLAGS, | |
| 30 | + NFACCT_QUOTA, | |
| 22 | 31 | __NFACCT_MAX |
| 23 | 32 | }; |
| 24 | 33 | #define NFACCT_MAX (__NFACCT_MAX - 1) |
| @@ -91,17 +91,9 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops, · 90 unmodified lines | ||
| 91 | 91 | if (nf_ct_is_untracked(ct)) |
| 92 | 92 | return NF_ACCEPT; |
| 93 | 93 | |
| 94 | − nat = nfct_nat(ct); | |
| 95 | − if (!nat) { | |
| 96 | − /* NAT module was loaded late. */ | |
| 97 | − if (nf_ct_is_confirmed(ct)) | |
| 98 | − return NF_ACCEPT; | |
| 99 | − nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 100 | − if (nat == NULL) { | |
| 101 | − pr_debug("failed to add NAT extension\n"); | |
| 102 | − return NF_ACCEPT; | |
| 103 | − } | |
| 104 | − } | |
| 94 | + nat = nf_ct_nat_ext_add(ct); | |
| 95 | + if (nat == NULL) | |
| 96 | + return NF_ACCEPT; | |
| 105 | 97 | |
| 106 | 98 | switch (ctinfo) { |
| 107 | 99 | case IP_CT_RELATED: |
| @@ -48,15 +48,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, · 47 unmodified lines | ||
| 48 | 48 | |
| 49 | 49 | NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))); |
| 50 | 50 | |
| 51 | − nat = nfct_nat(ct); | |
| 52 | − if (nat == NULL) { | |
| 53 | − /* Conntrack module was loaded late, can't add extension. */ | |
| 54 | − if (nf_ct_is_confirmed(ct)) | |
| 55 | − return NF_ACCEPT; | |
| 56 | − nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 57 | − if (nat == NULL) | |
| 58 | − return NF_ACCEPT; | |
| 59 | − } | |
| 51 | + nat = nf_ct_nat_ext_add(ct); | |
| 52 | + if (nat == NULL) | |
| 53 | + return NF_ACCEPT; | |
| 60 | 54 | |
| 61 | 55 | switch (ctinfo) { |
| 62 | 56 | case IP_CT_RELATED: |
| @@ -90,17 +90,9 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops, · 89 unmodified lines | ||
| 90 | 90 | if (nf_ct_is_untracked(ct)) |
| 91 | 91 | return NF_ACCEPT; |
| 92 | 92 | |
| 93 | − nat = nfct_nat(ct); | |
| 94 | − if (!nat) { | |
| 95 | − /* NAT module was loaded late. */ | |
| 96 | − if (nf_ct_is_confirmed(ct)) | |
| 97 | − return NF_ACCEPT; | |
| 98 | − nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 99 | − if (nat == NULL) { | |
| 100 | − pr_debug("failed to add NAT extension\n"); | |
| 101 | − return NF_ACCEPT; | |
| 102 | − } | |
| 103 | − } | |
| 93 | + nat = nf_ct_nat_ext_add(ct); | |
| 94 | + if (nat == NULL) | |
| 95 | + return NF_ACCEPT; | |
| 104 | 96 | |
| 105 | 97 | switch (ctinfo) { |
| 106 | 98 | case IP_CT_RELATED: |
| @@ -47,15 +47,9 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, · 46 unmodified lines | ||
| 47 | 47 | if (ct == NULL || nf_ct_is_untracked(ct)) |
| 48 | 48 | return NF_ACCEPT; |
| 49 | 49 | |
| 50 | − nat = nfct_nat(ct); | |
| 51 | − if (nat == NULL) { | |
| 52 | − /* Conntrack module was loaded late, can't add extension. */ | |
| 53 | − if (nf_ct_is_confirmed(ct)) | |
| 54 | − return NF_ACCEPT; | |
| 55 | − nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 56 | − if (nat == NULL) | |
| 57 | − return NF_ACCEPT; | |
| 58 | − } | |
| 50 | + nat = nf_ct_nat_ext_add(ct); | |
| 51 | + if (nat == NULL) | |
| 52 | + return NF_ACCEPT; | |
| 59 | 53 | |
| 60 | 54 | switch (ctinfo) { |
| 61 | 55 | case IP_CT_RELATED: |
| @@ -97,7 +97,7 @@ const char *ip_vs_proto_name(unsigned int proto) · 96 unmodified lines | ||
| 97 | 97 | return "ICMPv6"; |
| 98 | 98 | #endif |
| 99 | 99 | default: |
| 100 | − sprintf(buf, "IP_%d", proto); | |
| 100 | + sprintf(buf, "IP_%u", proto); | |
| 101 | 101 | return buf; |
| 102 | 102 | } |
| 103 | 103 | } |
| @@ -358,6 +358,19 @@ out: · 357 unmodified lines | ||
| 358 | 358 | rcu_read_unlock(); |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | +struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct) | |
| 362 | +{ | |
| 363 | + struct nf_conn_nat *nat = nfct_nat(ct); | |
| 364 | + if (nat) | |
| 365 | + return nat; | |
| 366 | + | |
| 367 | + if (!nf_ct_is_confirmed(ct)) | |
| 368 | + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 369 | + | |
| 370 | + return nat; | |
| 371 | +} | |
| 372 | +EXPORT_SYMBOL_GPL(nf_ct_nat_ext_add); | |
| 373 | + | |
| 361 | 374 | unsigned int |
| 362 | 375 | nf_nat_setup_info(struct nf_conn *ct, |
| 363 | 376 | const struct nf_nat_range *range, |
| @@ -368,14 +381,9 @@ nf_nat_setup_info(struct nf_conn *ct, · 4 unmodified lines | ||
| 368 | 381 | struct nf_conn_nat *nat; |
| 369 | 382 | |
| 370 | 383 | /* nat helper or nfctnetlink also setup binding */ |
| 371 | − nat = nfct_nat(ct); | |
| 372 | − if (!nat) { | |
| 373 | − nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | |
| 374 | − if (nat == NULL) { | |
| 375 | − pr_debug("failed to add NAT extension\n"); | |
| 376 | − return NF_ACCEPT; | |
| 377 | − } | |
| 378 | − } | |
| 384 | + nat = nf_ct_nat_ext_add(ct); | |
| 385 | + if (nat == NULL) | |
| 386 | + return NF_ACCEPT; | |
| 379 | 387 | |
| 380 | 388 | NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || |
| 381 | 389 | maniptype == NF_NAT_MANIP_DST); |
| @@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list); · 31 unmodified lines | ||
| 32 | 32 | struct nf_acct { |
| 33 | 33 | atomic64_t pkts; |
| 34 | 34 | atomic64_t bytes; |
| 35 | + unsigned long flags; | |
| 35 | 36 | struct list_head head; |
| 36 | 37 | atomic_t refcnt; |
| 37 | 38 | char name[NFACCT_NAME_MAX]; |
| 38 | 39 | struct rcu_head rcu_head; |
| 40 | + char data[0]; | |
| 39 | 41 | }; |
| 40 | 42 | |
| 43 | +#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) | |
| 44 | + | |
| 41 | 45 | static int |
| 42 | 46 | nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, |
| 43 | 47 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
| 44 | 48 | { |
| 45 | 49 | struct nf_acct *nfacct, *matching = NULL; |
| 46 | 50 | char *acct_name; |
| 51 | + unsigned int size = 0; | |
| 52 | + u32 flags = 0; | |
| 47 | 53 | |
| 48 | 54 | if (!tb[NFACCT_NAME]) |
| 49 | 55 | return -EINVAL; |
| @@ -68,15 +74,39 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, · 18 unmodified lines | ||
| 68 | 74 | /* reset counters if you request a replacement. */ |
| 69 | 75 | atomic64_set(&matching->pkts, 0); |
| 70 | 76 | atomic64_set(&matching->bytes, 0); |
| 77 | + smp_mb__before_clear_bit(); | |
| 78 | + /* reset overquota flag if quota is enabled. */ | |
| 79 | + if ((matching->flags & NFACCT_F_QUOTA)) | |
| 80 | + clear_bit(NFACCT_F_OVERQUOTA, &matching->flags); | |
| 71 | 81 | return 0; |
| 72 | 82 | } |
| 73 | 83 | return -EBUSY; |
| 74 | 84 | } |
| 75 | 85 | |
| 76 | 86 | nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL); |
| 87 | + if (tb[NFACCT_FLAGS]) { | |
| 88 | + flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); | |
| 89 | + if (flags & ~NFACCT_F_QUOTA) | |
| 90 | + return -EOPNOTSUPP; | |
| 91 | + if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) | |
| 92 | + return -EINVAL; | |
| 93 | + if (flags & NFACCT_F_OVERQUOTA) | |
| 94 | + return -EINVAL; | |
| 95 | + | |
| 96 | + size += sizeof(u64); | |
| 97 | + } | |
| 98 | + | |
| 99 | + nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); | |
| 77 | 100 | if (nfacct == NULL) |
| 78 | 101 | return -ENOMEM; |
| 79 | 102 | |
| 103 | + if (flags & NFACCT_F_QUOTA) { | |
| 104 | + u64 *quota = (u64 *)nfacct->data; | |
| 105 | + | |
| 106 | + *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); | |
| 107 | + nfacct->flags = flags; | |
| 108 | + } | |
| 109 | + | |
| 80 | 110 | strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); |
| 81 | 111 | |
| 82 | 112 | if (tb[NFACCT_BYTES]) { |
| @@ -117,6 +147,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, · 34 unmodified lines | ||
| 117 | 147 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { |
| 118 | 148 | pkts = atomic64_xchg(&acct->pkts, 0); |
| 119 | 149 | bytes = atomic64_xchg(&acct->bytes, 0); |
| 150 | + smp_mb__before_clear_bit(); | |
| 151 | + if (acct->flags & NFACCT_F_QUOTA) | |
| 152 | + clear_bit(NFACCT_F_OVERQUOTA, &acct->flags); | |
| 120 | 153 | } else { |
| 121 | 154 | pkts = atomic64_read(&acct->pkts); |
| 122 | 155 | bytes = atomic64_read(&acct->bytes); |
| @@ -125,7 +158,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, · 2 unmodified lines | ||
| 125 | 158 | nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || |
| 126 | 159 | nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) |
| 127 | 160 | goto nla_put_failure; |
| 161 | + if (acct->flags & NFACCT_F_QUOTA) { | |
| 162 | + u64 *quota = (u64 *)acct->data; | |
| 128 | 163 | |
| 164 | + if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) || | |
| 165 | + nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) | |
| 166 | + goto nla_put_failure; | |
| 167 | + } | |
| 129 | 168 | nlmsg_end(skb, nlh); |
| 130 | 169 | return skb->len; |
| 131 | 170 | |
| @@ -270,6 +309,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { · 138 unmodified lines | ||
| 270 | 309 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, |
| 271 | 310 | [NFACCT_BYTES] = { .type = NLA_U64 }, |
| 272 | 311 | [NFACCT_PKTS] = { .type = NLA_U64 }, |
| 312 | + [NFACCT_FLAGS] = { .type = NLA_U32 }, | |
| 313 | + [NFACCT_QUOTA] = { .type = NLA_U64 }, | |
| 273 | 314 | }; |
| 274 | 315 | |
| 275 | 316 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { |
| @@ -336,6 +377,50 @@ void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) · 60 unmodified lines | ||
| 336 | 377 | } |
| 337 | 378 | EXPORT_SYMBOL_GPL(nfnl_acct_update); |
| 338 | 379 | |
| 380 | +static void nfnl_overquota_report(struct nf_acct *nfacct) | |
| 381 | +{ | |
| 382 | + int ret; | |
| 383 | + struct sk_buff *skb; | |
| 384 | + | |
| 385 | + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | |
| 386 | + if (skb == NULL) | |
| 387 | + return; | |
| 388 | + | |
| 389 | + ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, | |
| 390 | + nfacct); | |
| 391 | + if (ret <= 0) { | |
| 392 | + kfree_skb(skb); | |
| 393 | + return; | |
| 394 | + } | |
| 395 | + netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, | |
| 396 | + GFP_ATOMIC); | |
| 397 | +} | |
| 398 | + | |
| 399 | +int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) | |
| 400 | +{ | |
| 401 | + u64 now; | |
| 402 | + u64 *quota; | |
| 403 | + int ret = NFACCT_UNDERQUOTA; | |
| 404 | + | |
| 405 | + /* no place here if we don't have a quota */ | |
| 406 | + if (!(nfacct->flags & NFACCT_F_QUOTA)) | |
| 407 | + return NFACCT_NO_QUOTA; | |
| 408 | + | |
| 409 | + quota = (u64 *)nfacct->data; | |
| 410 | + now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? | |
| 411 | + atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); | |
| 412 | + | |
| 413 | + ret = now > *quota; | |
| 414 | + | |
| 415 | + if (now >= *quota && | |
| 416 | + !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) { | |
| 417 | + nfnl_overquota_report(nfacct); | |
| 418 | + } | |
| 419 | + | |
| 420 | + return ret; | |
| 421 | +} | |
| 422 | +EXPORT_SYMBOL_GPL(nfnl_acct_overquota); | |
| 423 | + | |
| 339 | 424 | static int __init nfnl_acct_init(void) |
| 340 | 425 | { |
| 341 | 426 | int ret; |
| @@ -21,11 +21,14 @@ MODULE_ALIAS("ip6t_nfacct"); · 20 unmodified lines | ||
| 21 | 21 | |
| 22 | 22 | static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par) |
| 23 | 23 | { |
| 24 | + int overquota; | |
| 24 | 25 | const struct xt_nfacct_match_info *info = par->targinfo; |
| 25 | 26 | |
| 26 | 27 | nfnl_acct_update(skb, info->nfacct); |
| 27 | 28 | |
| 28 | − return true; | |
| 29 | + overquota = nfnl_acct_overquota(skb, info->nfacct); | |
| 30 | + | |
| 31 | + return overquota == NFACCT_UNDERQUOTA ? false : true; | |
| 29 | 32 | } |
| 30 | 33 | |
| 31 | 34 | static int |