|
@@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list);
|
|
|
struct nf_acct {
|
|
|
atomic64_t pkts;
|
|
|
atomic64_t bytes;
|
|
|
+ unsigned long flags;
|
|
|
struct list_head head;
|
|
|
atomic_t refcnt;
|
|
|
char name[NFACCT_NAME_MAX];
|
|
|
struct rcu_head rcu_head;
|
|
|
+ char data[0];
|
|
|
};
|
|
|
|
|
|
+#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
|
|
|
+
|
|
|
static int
|
|
|
nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
|
|
|
const struct nlmsghdr *nlh, const struct nlattr * const tb[])
|
|
|
{
|
|
|
struct nf_acct *nfacct, *matching = NULL;
|
|
|
char *acct_name;
|
|
|
+ unsigned int size = 0;
|
|
|
+ u32 flags = 0;
|
|
|
|
|
|
if (!tb[NFACCT_NAME])
|
|
|
return -EINVAL;
|
|
@@ -68,15 +74,39 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
|
|
|
/* reset counters if you request a replacement. */
|
|
|
atomic64_set(&matching->pkts, 0);
|
|
|
atomic64_set(&matching->bytes, 0);
|
|
|
+ smp_mb__before_clear_bit();
|
|
|
+ /* reset overquota flag if quota is enabled. */
|
|
|
+ if ((matching->flags & NFACCT_F_QUOTA))
|
|
|
+ clear_bit(NFACCT_F_OVERQUOTA, &matching->flags);
|
|
|
return 0;
|
|
|
}
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
|
|
|
nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL);
|
|
|
+ if (tb[NFACCT_FLAGS]) {
|
|
|
+ flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS]));
|
|
|
+ if (flags & ~NFACCT_F_QUOTA)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA)
|
|
|
+ return -EINVAL;
|
|
|
+ if (flags & NFACCT_F_OVERQUOTA)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ size += sizeof(u64);
|
|
|
+ }
|
|
|
+
|
|
|
+ nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL);
|
|
|
if (nfacct == NULL)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ if (flags & NFACCT_F_QUOTA) {
|
|
|
+ u64 *quota = (u64 *)nfacct->data;
|
|
|
+
|
|
|
+ *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA]));
|
|
|
+ nfacct->flags = flags;
|
|
|
+ }
|
|
|
+
|
|
|
strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
|
|
|
|
|
|
if (tb[NFACCT_BYTES]) {
|
|
@@ -117,6 +147,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
|
|
|
if (type == NFNL_MSG_ACCT_GET_CTRZERO) {
|
|
|
pkts = atomic64_xchg(&acct->pkts, 0);
|
|
|
bytes = atomic64_xchg(&acct->bytes, 0);
|
|
|
+ smp_mb__before_clear_bit();
|
|
|
+ if (acct->flags & NFACCT_F_QUOTA)
|
|
|
+ clear_bit(NFACCT_F_OVERQUOTA, &acct->flags);
|
|
|
} else {
|
|
|
pkts = atomic64_read(&acct->pkts);
|
|
|
bytes = atomic64_read(&acct->bytes);
|
|
@@ -125,7 +158,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
|
|
|
nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) ||
|
|
|
nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt))))
|
|
|
goto nla_put_failure;
|
|
|
+ if (acct->flags & NFACCT_F_QUOTA) {
|
|
|
+ u64 *quota = (u64 *)acct->data;
|
|
|
|
|
|
+ if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) ||
|
|
|
+ nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota)))
|
|
|
+ goto nla_put_failure;
|
|
|
+ }
|
|
|
nlmsg_end(skb, nlh);
|
|
|
return skb->len;
|
|
|
|
|
@@ -270,6 +309,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
|
|
|
[NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
|
|
|
[NFACCT_BYTES] = { .type = NLA_U64 },
|
|
|
[NFACCT_PKTS] = { .type = NLA_U64 },
|
|
|
+ [NFACCT_FLAGS] = { .type = NLA_U32 },
|
|
|
+ [NFACCT_QUOTA] = { .type = NLA_U64 },
|
|
|
};
|
|
|
|
|
|
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)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(nfnl_acct_update);
|
|
|
|
|
|
+static void nfnl_overquota_report(struct nf_acct *nfacct)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct sk_buff *skb;
|
|
|
+
|
|
|
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
|
|
+ if (skb == NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0,
|
|
|
+ nfacct);
|
|
|
+ if (ret <= 0) {
|
|
|
+ kfree_skb(skb);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA,
|
|
|
+ GFP_ATOMIC);
|
|
|
+}
|
|
|
+
|
|
|
+int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct)
|
|
|
+{
|
|
|
+ u64 now;
|
|
|
+ u64 *quota;
|
|
|
+ int ret = NFACCT_UNDERQUOTA;
|
|
|
+
|
|
|
+ /* no place here if we don't have a quota */
|
|
|
+ if (!(nfacct->flags & NFACCT_F_QUOTA))
|
|
|
+ return NFACCT_NO_QUOTA;
|
|
|
+
|
|
|
+ quota = (u64 *)nfacct->data;
|
|
|
+ now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ?
|
|
|
+ atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes);
|
|
|
+
|
|
|
+ ret = now > *quota;
|
|
|
+
|
|
|
+ if (now >= *quota &&
|
|
|
+ !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) {
|
|
|
+ nfnl_overquota_report(nfacct);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(nfnl_acct_overquota);
|
|
|
+
|
|
|
static int __init nfnl_acct_init(void)
|
|
|
{
|
|
|
int ret;
|