diff options
Diffstat (limited to 'net/netfilter/nfnetlink_acct.c')
| -rw-r--r-- | net/netfilter/nfnetlink_acct.c | 86 | 
1 files changed, 85 insertions, 1 deletions
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index c7b6d466a66..2baa125c2e8 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -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,38 @@ 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_atomic(); +			/* 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 +146,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_atomic(); +		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 +157,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 +308,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 +376,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;  | 
