netfilter: xt_quota2: 3.18 netlink notification fix
Create a virtual device inside of sysfs. Use the created kobject to notify userspace of counter transitions. Bug: 24140541 CRs-Fixed: 1008025 Change-Id: I06a02a3e6c70160083e17291cf08f1e9b375a26f Signed-off-by: Bryse Flowers <bflowers@codeaurora.org>
This commit is contained in:
parent
ab26d09879
commit
f889e67f37
1 changed files with 60 additions and 74 deletions
|
@ -16,14 +16,15 @@
|
|||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <net/netlink.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_quota2.h>
|
||||
#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
|
||||
#include <linux/netfilter_ipv4/ipt_ULOG.h>
|
||||
#endif
|
||||
|
||||
#define QUOTA2_SYSFS_WORK_MAX_SIZE 64
|
||||
#define QUOTA2_SYSFS_NUM_ENVP 3
|
||||
|
||||
/**
|
||||
* @lock: lock to protect quota writers from each other
|
||||
|
@ -35,17 +36,16 @@ struct xt_quota_counter {
|
|||
atomic_t ref;
|
||||
char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)];
|
||||
struct proc_dir_entry *procfs_entry;
|
||||
char last_iface[QUOTA2_SYSFS_WORK_MAX_SIZE];
|
||||
char last_prefix[QUOTA2_SYSFS_WORK_MAX_SIZE];
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
|
||||
/* Harald's favorite number +1 :D From ipt_ULOG.C */
|
||||
static int qlog_nl_event = 112;
|
||||
module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(event_num,
|
||||
"Event number for NETLINK_NFLOG message. 0 disables log."
|
||||
"111 is what ipt_ULOG uses.");
|
||||
static struct sock *nflognl;
|
||||
#endif
|
||||
#define to_quota_counter(x) container_of(x, struct xt_quota_counter, work)
|
||||
|
||||
static struct class *quota_class;
|
||||
static struct device *quota_device;
|
||||
static struct kobject *quota_kobj;
|
||||
|
||||
static LIST_HEAD(counter_list);
|
||||
static DEFINE_SPINLOCK(counter_list_lock);
|
||||
|
@ -56,68 +56,39 @@ static kuid_t quota_list_uid = KUIDT_INIT(0);
|
|||
static kgid_t quota_list_gid = KGIDT_INIT(0);
|
||||
module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
|
||||
|
||||
#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
|
||||
static void quota2_log(unsigned int hooknum,
|
||||
const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
static void quota2_work(struct work_struct *work)
|
||||
{
|
||||
char alert_msg[QUOTA2_SYSFS_WORK_MAX_SIZE];
|
||||
char iface_name[QUOTA2_SYSFS_WORK_MAX_SIZE];
|
||||
char *envp[QUOTA2_SYSFS_NUM_ENVP] = {alert_msg, iface_name, NULL};
|
||||
struct xt_quota_counter *counter = to_quota_counter(work);
|
||||
|
||||
snprintf(alert_msg, sizeof(alert_msg), "ALERT_NAME=%s", counter->name);
|
||||
snprintf(iface_name, sizeof(iface_name), "INTERFACE=%s",
|
||||
counter->last_iface);
|
||||
|
||||
kobject_uevent_env(quota_kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
static void quota2_log(const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
struct xt_quota_counter *q,
|
||||
const char *prefix)
|
||||
{
|
||||
ulog_packet_msg_t *pm;
|
||||
struct sk_buff *log_skb;
|
||||
size_t size;
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
if (!qlog_nl_event)
|
||||
if (!prefix)
|
||||
return;
|
||||
|
||||
size = NLMSG_SPACE(sizeof(*pm));
|
||||
size = max(size, (size_t)NLMSG_GOODSIZE);
|
||||
log_skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (!log_skb) {
|
||||
pr_err("xt_quota2: cannot alloc skb for logging\n");
|
||||
return;
|
||||
}
|
||||
strlcpy(q->last_prefix, prefix, QUOTA2_SYSFS_WORK_MAX_SIZE);
|
||||
|
||||
nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event,
|
||||
sizeof(*pm), 0);
|
||||
if (!nlh) {
|
||||
pr_err("xt_quota2: nlmsg_put failed\n");
|
||||
kfree_skb(log_skb);
|
||||
return;
|
||||
}
|
||||
pm = nlmsg_data(nlh);
|
||||
if (skb->tstamp.tv64 == 0)
|
||||
__net_timestamp((struct sk_buff *)skb);
|
||||
pm->data_len = 0;
|
||||
pm->hook = hooknum;
|
||||
if (prefix != NULL)
|
||||
strlcpy(pm->prefix, prefix, sizeof(pm->prefix));
|
||||
else
|
||||
*(pm->prefix) = '\0';
|
||||
if (in)
|
||||
strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name));
|
||||
strlcpy(q->last_iface, in->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
|
||||
else if (out)
|
||||
strlcpy(q->last_iface, out->name, QUOTA2_SYSFS_WORK_MAX_SIZE);
|
||||
else
|
||||
pm->indev_name[0] = '\0';
|
||||
strlcpy(q->last_iface, "UNKNOWN", QUOTA2_SYSFS_WORK_MAX_SIZE);
|
||||
|
||||
if (out)
|
||||
strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
|
||||
else
|
||||
pm->outdev_name[0] = '\0';
|
||||
|
||||
NETLINK_CB(log_skb).dst_group = 1;
|
||||
pr_debug("throwing 1 packets to netlink group 1\n");
|
||||
netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC);
|
||||
schedule_work(&q->work);
|
||||
}
|
||||
#else
|
||||
static void quota2_log(unsigned int hooknum,
|
||||
const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const char *prefix)
|
||||
{
|
||||
}
|
||||
#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */
|
||||
|
||||
static ssize_t quota_proc_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *ppos)
|
||||
|
@ -174,6 +145,9 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon)
|
|||
INIT_LIST_HEAD(&e->list);
|
||||
atomic_set(&e->ref, 1);
|
||||
strlcpy(e->name, q->name, sizeof(e->name));
|
||||
strlcpy(e->last_prefix, "UNSET", sizeof(e->last_prefix));
|
||||
strlcpy(e->last_iface, "UNSET", sizeof(e->last_iface));
|
||||
INIT_WORK(&e->work, quota2_work);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
@ -307,11 +281,7 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
|
|||
} else {
|
||||
/* We are transitioning, log that fact. */
|
||||
if (e->quota) {
|
||||
quota2_log(par->hooknum,
|
||||
skb,
|
||||
par->in,
|
||||
par->out,
|
||||
q->name);
|
||||
quota2_log(par->in, par->out, e, q->name);
|
||||
}
|
||||
/* we do not allow even small packets from now on */
|
||||
e->quota = 0;
|
||||
|
@ -349,11 +319,25 @@ static int __init quota_mt2_init(void)
|
|||
int ret;
|
||||
pr_debug("xt_quota2: init()");
|
||||
|
||||
#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG
|
||||
nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL);
|
||||
if (!nflognl)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
quota_class = class_create(THIS_MODULE, "xt_quota2");
|
||||
ret = PTR_ERR(quota_class);
|
||||
if (IS_ERR(quota_class)) {
|
||||
pr_err("xt_quota2: couldn't create class");
|
||||
class_destroy(quota_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
quota_device = device_create(quota_class, NULL, MKDEV(0, 0), NULL,
|
||||
"counters");
|
||||
ret = PTR_ERR(quota_device);
|
||||
if (IS_ERR(quota_device)) {
|
||||
pr_err("xt_quota2: couldn't create device");
|
||||
device_destroy(quota_class, MKDEV(0, 0));
|
||||
class_destroy(quota_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
quota_kobj = "a_device->kobj;
|
||||
|
||||
proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
|
||||
if (proc_xt_quota == NULL)
|
||||
|
@ -370,6 +354,8 @@ static void __exit quota_mt2_exit(void)
|
|||
{
|
||||
xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg));
|
||||
remove_proc_entry("xt_quota", init_net.proc_net);
|
||||
device_destroy(quota_class, MKDEV(0, 0));
|
||||
class_destroy(quota_class);
|
||||
}
|
||||
|
||||
module_init(quota_mt2_init);
|
||||
|
|
Loading…
Add table
Reference in a new issue