summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2011-07-25 09:58:01 -0700
committerJohn Stultz <john.stultz@linaro.org>2011-07-25 09:58:01 -0700
commit1a3807e5a6bea7e4b195fbb399bbc09e73230d4c (patch)
tree18ec7d938c8c607b7e779d099d779fabfc1698fd
parent0e4ec5c1011bf87326168a9b51bf22852747bf93 (diff)
parent8c59c45bc55f052d7d767d93c88a6f1666dcdecf (diff)
Merge branch 'upstream/android-3.0' into linaro-android-3.0
-rw-r--r--include/linux/android_aid.h2
-rw-r--r--net/netfilter/Kconfig12
-rw-r--r--net/netfilter/xt_qtaguid.c535
-rw-r--r--net/netfilter/xt_quota2.c92
4 files changed, 498 insertions, 143 deletions
diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h
index 7f16a14c0fe..0f904b3ba7f 100644
--- a/include/linux/android_aid.h
+++ b/include/linux/android_aid.h
@@ -22,5 +22,7 @@
#define AID_INET 3003
#define AID_NET_RAW 3004
#define AID_NET_ADMIN 3005
+#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
+#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#endif
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index ddb7bb507bd..5bd5c612a9b 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -975,6 +975,18 @@ config NETFILTER_XT_MATCH_QUOTA2
If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
+config NETFILTER_XT_MATCH_QUOTA2_LOG
+ bool '"quota2" Netfilter LOG support'
+ depends on NETFILTER_XT_MATCH_QUOTA2
+ depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no
+ default n
+ help
+ This option allows `quota2' to log ONCE when a quota limit
+ is passed. It logs via NETLINK using the NETLINK_NFLOG family.
+ It logs similarly to how ipt_ULOG would without data.
+
+ If unsure, say `N'.
+
config NETFILTER_XT_MATCH_RATEEST
tristate '"rateest" match support'
depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 49ed432d793..22552c9b81c 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -8,7 +8,8 @@
* published by the Free Software Foundation.
*/
-/* TODO: support ipv6 for iface_stat */
+/* TODO: support ipv6 for iface_stat.
+ * Currently if an iface is only v6 it will not have stats collected. */
#include <linux/file.h>
#include <linux/inetdevice.h>
@@ -28,6 +29,49 @@
((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN))
+static const char *module_procdirname = "xt_qtaguid";
+static struct proc_dir_entry *xt_qtaguid_procdir;
+
+static unsigned int proc_iface_perms = S_IRUGO;
+module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR);
+
+static struct proc_dir_entry *xt_qtaguid_stats_file;
+static unsigned int proc_stats_perms = S_IRUGO;
+module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR);
+
+static struct proc_dir_entry *xt_qtaguid_ctrl_file;
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO;
+#else
+static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR;
+#endif
+module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR);
+
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+#include <linux/android_aid.h>
+static gid_t proc_stats_readall_gid = AID_NET_BW_STATS;
+static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT;
+#else
+/* 0 means, don't limit anybody */
+static gid_t proc_stats_readall_gid;
+static gid_t proc_ctrl_write_gid;
+#endif
+module_param_named(stats_readall_gid, proc_stats_readall_gid, uint,
+ S_IRUGO | S_IWUSR);
+module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint,
+ S_IRUGO | S_IWUSR);
+
+/* After the kernel has initiallized this module, it is still possible
+ * to make it passive:
+ * - do not register it via iptables.
+ * the matching code will not be invoked.
+ * - set passive to 0
+ * the iface stats handling will not be act on notifications.
+ * This is mostly usefull when a bug is suspected.
+ */
+static bool module_passive;
+module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR);
+
/*---------------------------------------------------------------------------*/
/*
* Tags:
@@ -96,9 +140,6 @@ struct tag_stat {
struct proc_dir_entry *proc_ptr;
};
-static LIST_HEAD(iface_stat_list);
-static DEFINE_SPINLOCK(iface_stat_list_lock);
-
struct iface_stat {
struct list_head list;
char *ifname;
@@ -113,9 +154,8 @@ struct iface_stat {
spinlock_t tag_stat_list_lock;
};
-
-static struct rb_root sock_tag_tree = RB_ROOT;
-static DEFINE_SPINLOCK(sock_tag_list_lock);
+static LIST_HEAD(iface_stat_list);
+static DEFINE_SPINLOCK(iface_stat_list_lock);
/*
* Track tag that this socket is transferring data for, and not necesseraly
@@ -128,6 +168,9 @@ struct sock_tag {
tag_t tag;
};
+static struct rb_root sock_tag_tree = RB_ROOT;
+static DEFINE_SPINLOCK(sock_tag_list_lock);
+
static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par);
/*----------------------------------------------*/
@@ -181,6 +224,14 @@ static inline uint64_t dc_sum_bytes(struct data_counters *counters,
+ counters->bpc[direction][IFS_PROTO_OTHER].bytes;
}
+static inline uint64_t dc_sum_packets(struct data_counters *counters,
+ enum ifs_tx_rx direction)
+{
+ return counters->bpc[direction][IFS_TCP].packets
+ + counters->bpc[direction][IFS_UDP].packets
+ + counters->bpc[direction][IFS_PROTO_OTHER].packets;
+}
+
static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag)
{
struct rb_node *node = root->rb_node;
@@ -397,12 +448,11 @@ void iface_stat_create(const struct net_device *net_dev)
return;
}
- new_iface = kmalloc(sizeof(*new_iface), GFP_KERNEL);
+ new_iface = kzalloc(sizeof(*new_iface), GFP_KERNEL);
if (new_iface == NULL) {
pr_err("iface_stat: create(): failed to alloc iface_stat\n");
return;
}
- memset(new_iface, 0, sizeof(*new_iface));
new_iface->ifname = kstrdup(ifname, GFP_KERNEL);
if (new_iface->ifname == NULL) {
pr_err("iface_stat: create(): failed to alloc ifname\n");
@@ -422,15 +472,15 @@ void iface_stat_create(const struct net_device *net_dev)
new_iface->proc_ptr = proc_entry;
/* TODO: make root access only */
- create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry,
+ create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry,
read_proc_u64, &new_iface->tx_bytes);
- create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry,
+ create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry,
read_proc_u64, &new_iface->rx_bytes);
- create_proc_read_entry("tx_packets", S_IRUGO, proc_entry,
+ create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry,
read_proc_u64, &new_iface->tx_packets);
- create_proc_read_entry("rx_packets", S_IRUGO, proc_entry,
+ create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry,
read_proc_u64, &new_iface->rx_packets);
- create_proc_read_entry("active", S_IRUGO, proc_entry,
+ create_proc_read_entry("active", proc_iface_perms, proc_entry,
read_proc_bool, &new_iface->active);
pr_debug("iface_stat: create(): done entry=%p dev=%s ip=%pI4\n",
@@ -531,12 +581,11 @@ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry,
pr_debug("iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx"
" (uid=%d)\n",
iface_entry, tag, get_uid_from_tag(tag));
- new_tag_stat_entry = kmalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC);
+ new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC);
if (!new_tag_stat_entry) {
pr_err("iface_stat: failed to alloc new tag entry\n");
goto done;
}
- memset(new_tag_stat_entry, 0, sizeof(*new_tag_stat_entry));
new_tag_stat_entry->tag = tag;
tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree);
done:
@@ -649,6 +698,9 @@ static int iface_netdev_event_handler(struct notifier_block *nb,
unsigned long event, void *ptr) {
struct net_device *dev = ptr;
+ if (unlikely(module_passive))
+ return NOTIFY_DONE;
+
pr_debug("iface_stat: netdev_event(): ev=0x%lx netdev=%p->name=%s\n",
event, dev, dev ? dev->name : "");
@@ -676,6 +728,9 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb,
struct in_device *in_dev = ifa->ifa_dev;
struct net_device *dev = in_dev->dev;
+ if (unlikely(module_passive))
+ return NOTIFY_DONE;
+
pr_debug("iface_stat: inetaddr_event(): ev=0x%lx netdev=%p->name=%s\n",
event, dev, dev ? dev->name : "");
@@ -811,8 +866,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
struct sock *sk;
uid_t sock_uid;
bool res;
+
pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n",
par->hooknum, skb, par->in, par->out, par->family);
+
if (skb == NULL) {
res = (info->match ^ info->invert) == 0;
goto ret_res;
@@ -852,7 +909,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
pr_debug("xt_qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
par->hooknum,
sk ? sk->sk_socket : NULL);
- res = (info->match ^ info->invert) == 0;
+ res = (info->match ^ info->invert) == 0;
goto put_sock_ret_res;
} else if (info->match & info->invert & XT_QTAGUID_SOCKET) {
res = false;
@@ -922,7 +979,12 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
struct rb_node *node;
int item_index = 0;
- pr_debug("xt_qtaguid:proc ctrl page=%p off=%ld char_count=%d *eof=%d\n",
+ if (unlikely(module_passive)) {
+ *eof = 1;
+ return 0;
+ }
+
+ pr_debug("xt_qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n",
page, items_to_skip, char_count, *eof);
if (*eof)
@@ -934,7 +996,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
node = rb_next(node)) {
if (item_index++ < items_to_skip)
continue;
- sock_tag_entry = rb_entry(node, struct sock_tag, node);
+ sock_tag_entry = rb_entry(node, struct sock_tag, node);
uid = get_uid_from_tag(sock_tag_entry->tag);
pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n",
sock_tag_entry->sk,
@@ -957,7 +1019,120 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
return outp - page;
}
-static int qtaguid_ctrl_parse(const char *input, int count)
+int can_impersonate_uid(uid_t uid)
+{
+ return uid == current_fsuid()
+ || !proc_ctrl_write_gid
+ || in_egroup_p(proc_ctrl_write_gid);
+}
+
+int can_read_other_uid_stats(uid_t uid)
+{
+ return uid == current_fsuid()
+ || !proc_ctrl_write_gid
+ || in_egroup_p(proc_stats_readall_gid);
+}
+
+/* Delete socket tags, and stat tags associated with a given
+ * accouting tag and uid. */
+static int ctrl_cmd_delete(const char *input)
+{
+ char cmd;
+ uid_t uid = 0;
+ uid_t entry_uid;
+ tag_t acct_tag = 0;
+ tag_t tag;
+ int res, argc;
+ unsigned long flags, flags2;
+ struct iface_stat *iface_entry;
+ struct rb_node *node;
+ struct sock_tag *st_entry;
+ struct tag_stat *ts_entry;
+
+ pr_debug("xt_qtaguid: ctrl_delete(%s): entered\n", input);
+ argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid);
+ pr_debug("xt_qtaguid: ctrl_delete(%s): argc=%d cmd=%c "
+ "acct_tag=0x%llx uid=%u\n", input, argc, cmd,
+ acct_tag, uid);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ if (!valid_atag(acct_tag)) {
+ pr_info("xt_qtaguid: ctrl_delete(%s): invalid tag\n", input);
+ res = -EINVAL;
+ goto err;
+ }
+ if (argc < 3) {
+ uid = current_fsuid();
+ } else if (!can_impersonate_uid(uid)) {
+ pr_info("xt_qtaguid: ctrl_delete(%s): insuficient priv\n",
+ input);
+ res = -EPERM;
+ goto err;
+ }
+
+ spin_lock_irqsave(&sock_tag_list_lock, flags);
+ node = rb_first(&sock_tag_tree);
+ while (node) {
+ st_entry = rb_entry(node, struct sock_tag, node);
+ entry_uid = get_uid_from_tag(st_entry->tag);
+ node = rb_next(node);
+ if (entry_uid != uid)
+ continue;
+
+ if (!acct_tag || st_entry->tag == tag) {
+ pr_debug("xt_qtaguid: ctrl_delete(): "
+ "erase sk=%p tag=0x%llx (uid=%d)\n",
+ st_entry->sk,
+ st_entry->tag,
+ entry_uid);
+ rb_erase(&ts_entry->node, &sock_tag_tree);
+ kfree(st_entry);
+ }
+ }
+ spin_unlock_irqrestore(&sock_tag_list_lock, flags);
+
+ /* If acct_tag is 0, then all entries belonging to uid are
+ * erased. */
+ tag = combine_atag_with_uid(acct_tag, uid);
+ spin_lock_irqsave(&iface_stat_list_lock, flags);
+ list_for_each_entry(iface_entry, &iface_stat_list, list) {
+
+ spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2);
+ node = rb_first(&iface_entry->tag_stat_tree);
+ while (node) {
+ ts_entry = rb_entry(node, struct tag_stat, node);
+ entry_uid = get_uid_from_tag(ts_entry->tag);
+ node = rb_next(node);
+ if (entry_uid != uid)
+ continue;
+ if (!acct_tag || ts_entry->tag == tag) {
+ pr_debug("xt_qtaguid: ctrl_delete(): erase "
+ "%s 0x%llx %u\n",
+ iface_entry->ifname,
+ get_atag_from_tag(ts_entry->tag),
+ entry_uid);
+ rb_erase(&ts_entry->node,
+ &iface_entry->tag_stat_tree);
+ kfree(ts_entry);
+ }
+ }
+ spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock,
+ flags2);
+
+ }
+ spin_unlock_irqrestore(&iface_stat_list_lock, flags);
+
+ res = 0;
+
+err:
+ pr_debug("xt_qtaguid: ctrl_delete(%s) res=%d\n", input, res);
+ return res;
+}
+
+
+static int ctrl_cmd_tag(const char *input)
{
char cmd;
int sock_fd = 0;
@@ -968,117 +1143,140 @@ static int qtaguid_ctrl_parse(const char *input, int count)
struct sock_tag *sock_tag_entry;
unsigned long flags;
- pr_debug("xt_qtaguid: ctrl(%s): entered\n", input);
/* Unassigned args will get defaulted later. */
- /* TODO: get acct_tag_str, keep a list of available tags for the
- * uid, use num as acct_tag. */
argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid);
- pr_debug("xt_qtaguid: ctrl(%s): argc=%d cmd=%c sock_fd=%d "
- "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd,
- acct_tag, uid);
+ pr_debug("xt_qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d "
+ "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd,
+ acct_tag, uid);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ el_socket = sockfd_lookup(sock_fd, &res);
+ if (!el_socket) {
+ pr_info("xt_qtaguid: ctrl_tag(%s): failed to lookup"
+ " sock_fd=%d err=%d\n", input, sock_fd, res);
+ goto err;
+ }
+ if (argc < 3) {
+ acct_tag = 0;
+ } else if (!valid_atag(acct_tag)) {
+ pr_info("xt_qtaguid: ctrl_tag(%s): invalid tag\n", input);
+ res = -EINVAL;
+ goto err;
+ }
+ if (argc < 4) {
+ uid = current_fsuid();
+ } else if (!can_impersonate_uid(uid)) {
+ pr_info("xt_qtaguid: ctrl_tag(%s): insuficient priv\n",
+ input);
+ res = -EPERM;
+ goto err;
+ }
- /* Collect params for commands */
- switch (cmd) {
- case 't':
- case 'u':
- if (argc < 2) {
- res = -EINVAL;
- goto err;
- }
- el_socket = sockfd_lookup(sock_fd, &res);
- if (!el_socket) {
- pr_info("xt_qtaguid: ctrl(%s): failed to lookup"
- " sock_fd=%d err=%d\n", input, sock_fd, res);
+ spin_lock_irqsave(&sock_tag_list_lock, flags);
+ sock_tag_entry = get_sock_stat_nl(el_socket->sk);
+ if (sock_tag_entry) {
+ sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
+ uid);
+ } else {
+ spin_unlock_irqrestore(&sock_tag_list_lock, flags);
+ sock_tag_entry = kzalloc(sizeof(*sock_tag_entry),
+ GFP_KERNEL);
+ if (!sock_tag_entry) {
+ res = -ENOMEM;
goto err;
}
+ sock_tag_entry->sk = el_socket->sk;
+ sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
+ uid);
spin_lock_irqsave(&sock_tag_list_lock, flags);
- /* TODO: optim: pass in the current_fsuid() to do lookups
- * as look ups will always be initiated form the same uid. */
- sock_tag_entry = get_sock_stat_nl(el_socket->sk);
- if (!sock_tag_entry)
- spin_unlock_irqrestore(&sock_tag_list_lock, flags);
- /* HERE: The lock is held if there was a matching sock tag entry */
- break;
- default:
+ sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree);
+ }
+ spin_unlock_irqrestore(&sock_tag_list_lock, flags);
+
+ pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p "
+ "...->tag=0x%llx (uid=%u)\n",
+ sock_tag_entry->sk, sock_tag_entry->tag,
+ get_uid_from_tag(sock_tag_entry->tag));
+ res = 0;
+
+err:
+ pr_debug("xt_qtaguid: ctrl_tag(%s) res=%d\n", input, res);
+ return res;
+}
+
+
+static int ctrl_cmd_untag(const char *input)
+{
+ char cmd;
+ int sock_fd = 0;
+ struct socket *el_socket;
+ int res, argc;
+ struct sock_tag *sock_tag_entry;
+ unsigned long flags;
+
+ pr_debug("xt_qtaguid: ctrl_untag(%s): entered\n", input);
+ argc = sscanf(input, "%c %d", &cmd, &sock_fd);
+ pr_debug("xt_qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n",
+ input, argc, cmd, sock_fd);
+ if (argc < 2) {
+ res = -EINVAL;
+ goto err;
+ }
+ el_socket = sockfd_lookup(sock_fd, &res);
+ if (!el_socket) {
+ pr_info("xt_qtaguid: ctrl_untag(%s): failed to lookup"
+ " sock_fd=%d err=%d\n", input, sock_fd, res);
+ goto err;
+ }
+ spin_lock_irqsave(&sock_tag_list_lock, flags);
+ sock_tag_entry = get_sock_stat_nl(el_socket->sk);
+ if (!sock_tag_entry) {
+ spin_unlock_irqrestore(&sock_tag_list_lock, flags);
res = -EINVAL;
goto err;
}
- /* HERE: The lock is held if there was a matching sock tag entry */
+ /* The socket already belongs to the current process
+ * so it can do whatever it wants to it. */
+ rb_erase(&sock_tag_entry->node, &sock_tag_tree);
+ spin_unlock_irqrestore(&sock_tag_list_lock, flags);
+ kfree(sock_tag_entry);
+
+ res = 0;
+err:
+ pr_debug("xt_qtaguid: ctrl_untag(%s): res=%d\n", input, res);
+ return res;
+}
+
+static int qtaguid_ctrl_parse(const char *input, int count)
+{
+ char cmd;
+ int res;
- /* Process commands */
+ pr_debug("xt_qtaguid: ctrl(%s): entered\n", input);
+ cmd = input[0];
+ /* Collect params for commands */
switch (cmd) {
+ case 'd':
+ res = ctrl_cmd_delete(input);
+ break;
case 't':
- if (argc < 2) {
- res = -EINVAL;
- /* HERE: The lock is held if there was a matching sock
- * tag entry */
- goto err_unlock;
- }
- if (argc < 3) {
- acct_tag = 0;
- } else if (!valid_atag(acct_tag)) {
- res = -EINVAL;
- /* HERE: The lock is held if there was a matching sock
- * tag entry */
- goto err_unlock;
- }
- if (argc < 4)
- uid = current_fsuid();
- if (!sock_tag_entry) {
- /* HERE: There is no lock held because there was no
- * sock tag entry */
- sock_tag_entry = kmalloc(sizeof(*sock_tag_entry),
- GFP_KERNEL);
- if (!sock_tag_entry) {
- res = -ENOMEM;
- goto err;
- }
- memset(sock_tag_entry, 0, sizeof(*sock_tag_entry));
- sock_tag_entry->sk = el_socket->sk;
- /* TODO: check that uid==current_fsuid() except
- * for special uid/gid. */
- sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
- uid);
- spin_lock_irqsave(&sock_tag_list_lock, flags);
- sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree);
- } else {
- /* HERE: The lock is held because there is a matching
- * sock tag entry */
- /* Just update the acct_tag portion. */
- uid_t orig_uid = get_uid_from_tag(sock_tag_entry->tag);
- sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
- orig_uid);
- }
- spin_unlock_irqrestore(&sock_tag_list_lock, flags);
- pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p "
- "...->tag=0x%llx (uid=%u)\n",
- sock_tag_entry->sk, sock_tag_entry->tag,
- get_uid_from_tag(sock_tag_entry->tag));
+ res = ctrl_cmd_tag(input);
break;
case 'u':
- if (!sock_tag_entry) {
- res = -EINVAL;
- goto err;
- }
- /* TODO: check that the uid==current_fsuid()
- * except for special uid/gid. */
- rb_erase(&sock_tag_entry->node, &sock_tag_tree);
- spin_unlock_irqrestore(&sock_tag_list_lock, flags);
- kfree(sock_tag_entry);
+ res = ctrl_cmd_untag(input);
break;
- }
-
- /* All of the input has been processed */
- res = count;
- goto ok;
-err_unlock:
- if (sock_tag_entry)
- spin_unlock_irqrestore(&sock_tag_list_lock, flags);
+ default:
+ res = -EINVAL;
+ goto err;
+ }
+ if (!res)
+ res = count;
err:
-ok:
pr_debug("xt_qtaguid: ctrl(%s): res=%d\n", input, res);
return res;
}
@@ -1089,6 +1287,9 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
{
char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN];
+ if (unlikely(module_passive))
+ return count;
+
if (count >= MAX_QTAGUID_CTRL_INPUT_LEN)
return -EINVAL;
@@ -1099,6 +1300,65 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
return qtaguid_ctrl_parse(input_buf, count);
}
+static int print_stats_line(char *outp, int char_count, int item_index,
+ char *ifname, tag_t tag,
+ struct data_counters *counters)
+{
+ int len;
+ if (!item_index) {
+ len = snprintf(outp, char_count,
+ "idx iface acct_tag_hex uid_tag_int "
+ "rx_bytes rx_packets "
+ "tx_bytes tx_packets "
+ "rx_tcp_packets rx_tcp_bytes "
+ "rx_udp_packets rx_udp_bytes "
+ "rx_other_packets rx_other_bytes "
+ "tx_tcp_packets tx_tcp_bytes "
+ "tx_udp_packets tx_udp_bytes "
+ "tx_other_packets tx_other_bytes\n");
+ } else {
+ uid_t stat_uid = get_uid_from_tag(tag);
+ if (!can_read_other_uid_stats(stat_uid)) {
+ pr_debug("xt_qtaguid: insufficient priv for stat line:"
+ "%s 0x%llx %u\n",
+ ifname, get_atag_from_tag(tag), stat_uid);
+ return 0;
+ }
+ len = snprintf(outp, char_count,
+ "%d %s 0x%llx %u "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu "
+ "%llu %llu\n",
+ item_index,
+ ifname,
+ get_atag_from_tag(tag),
+ stat_uid,
+ dc_sum_bytes(counters, IFS_RX),
+ dc_sum_packets(counters, IFS_RX),
+ dc_sum_bytes(counters, IFS_TX),
+ dc_sum_packets(counters, IFS_TX),
+ counters->bpc[IFS_RX][IFS_TCP].bytes,
+ counters->bpc[IFS_RX][IFS_TCP].packets,
+ counters->bpc[IFS_RX][IFS_UDP].bytes,
+ counters->bpc[IFS_RX][IFS_UDP].packets,
+ counters->bpc[IFS_RX][IFS_PROTO_OTHER].bytes,
+ counters->bpc[IFS_RX][IFS_PROTO_OTHER].packets,
+ counters->bpc[IFS_TX][IFS_TCP].bytes,
+ counters->bpc[IFS_TX][IFS_TCP].packets,
+ counters->bpc[IFS_TX][IFS_UDP].bytes,
+ counters->bpc[IFS_TX][IFS_UDP].packets,
+ counters->bpc[IFS_TX][IFS_PROTO_OTHER].bytes,
+ counters->bpc[IFS_TX][IFS_PROTO_OTHER].packets);
+ }
+ return len;
+}
+
+
/*
* Procfs reader to get all tag stats using style "1)" as described in
* fs/proc/generic.c
@@ -1115,7 +1375,10 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
struct tag_stat *ts_entry;
int item_index = 0;
- /* TODO: make root access only */
+ if (unlikely(module_passive)) {
+ *eof = 1;
+ return 0;
+ }
pr_debug("xt_qtaguid:proc stats page=%p *num_items_returned=%p off=%ld "
"char_count=%d *eof=%d\n", page, *num_items_returned,
@@ -1126,9 +1389,8 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
if (!items_to_skip) {
/* The idx is there to help debug when things go belly up. */
- len = snprintf(outp, char_count,
- "idx iface acct_tag_hex uid_tag_int rx_bytes "
- "tx_bytes\n");
+ len = print_stats_line(outp, char_count, /*index*/0, NULL,
+ make_tag_from_uid(0), NULL);
/* Don't advance the outp unless the whole line was printed */
if (len >= char_count) {
*outp = '\0';
@@ -1137,7 +1399,6 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
outp += len;
char_count -= len;
}
-
spin_lock_irqsave(&iface_stat_list_lock, flags);
list_for_each_entry(iface_entry, &iface_stat_list, list) {
struct rb_node *node;
@@ -1145,31 +1406,28 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
for (node = rb_first(&iface_entry->tag_stat_tree);
node;
node = rb_next(node)) {
- ts_entry = rb_entry(node, struct tag_stat, node);
+ ts_entry = rb_entry(node, struct tag_stat, node);
if (item_index++ < items_to_skip)
continue;
- len = snprintf(outp, char_count,
- "%d %s 0x%llx %u %llu %llu\n",
- item_index,
- iface_entry->ifname,
- get_atag_from_tag(ts_entry->tag),
- get_uid_from_tag(ts_entry->tag),
- dc_sum_bytes(&ts_entry->counters,
- IFS_RX),
- dc_sum_bytes(&ts_entry->counters,
- IFS_TX));
+ len = print_stats_line(outp, char_count,
+ item_index,
+ iface_entry->ifname,
+ ts_entry->tag,
+ &ts_entry->counters);
if (len >= char_count) {
+ *outp = '\0';
spin_unlock_irqrestore(
&iface_entry->tag_stat_list_lock,
flags2);
spin_unlock_irqrestore(
&iface_stat_list_lock, flags);
- *outp = '\0';
return outp - page;
}
- outp += len;
- char_count -= len;
- (*num_items_returned)++;
+ if (len) {
+ outp += len;
+ char_count -= len;
+ (*num_items_returned)++;
+ }
}
spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock,
flags2);
@@ -1181,11 +1439,6 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
}
/*------------------------------------------*/
-static const char *module_procdirname = "xt_qtaguid";
-static struct proc_dir_entry *xt_qtaguid_procdir;
-static struct proc_dir_entry *xt_qtaguid_ctrl_file;
-static struct proc_dir_entry *xt_qtaguid_stats_file;
-
static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
{
int ret;
@@ -1196,7 +1449,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
goto no_dir;
}
- xt_qtaguid_ctrl_file = create_proc_entry("ctrl", 0666,
+ xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms,
*res_procdir);
if (!xt_qtaguid_ctrl_file) {
pr_err("xt_qtaguid: failed to create xt_qtaguid/ctrl "
@@ -1207,7 +1460,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read;
xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write;
- xt_qtaguid_stats_file = create_proc_entry("stats", 0666,
+ xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms,
*res_procdir);
if (!xt_qtaguid_stats_file) {
pr_err("xt_qtaguid: failed to create xt_qtaguid/stats "
@@ -1215,10 +1468,6 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
ret = -ENOMEM;
goto no_stats_entry;
}
- /*
- * TODO: add extra read_proc for full stats with protocol
- * breakout
- */
xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read;
/*
* TODO: add support counter hacking
diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c
index 454ce10c8cd..3c72bea2dd6 100644
--- a/net/netfilter/xt_quota2.c
+++ b/net/netfilter/xt_quota2.c
@@ -19,6 +19,9 @@
#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
/**
* @lock: lock to protect quota writers from each other
@@ -32,6 +35,16 @@ struct xt_quota_counter {
struct proc_dir_entry *procfs_entry;
};
+#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
+
static LIST_HEAD(counter_list);
static DEFINE_SPINLOCK(counter_list_lock);
@@ -43,6 +56,69 @@ module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR);
module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR);
module_param_named(gid, quota_list_gid, 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,
+ const struct net_device *out,
+ const char *prefix)
+{
+ ulog_packet_msg_t *pm;
+ struct sk_buff *log_skb;
+ size_t size;
+ struct nlmsghdr *nlh;
+
+ if (!qlog_nl_event)
+ 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;
+ }
+
+ /* NLMSG_PUT() uses "goto nlmsg_failure" */
+ nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event,
+ sizeof(*pm));
+ 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));
+ else
+ pm->indev_name[0] = '\0';
+
+ 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);
+
+nlmsg_failure: /* Used within NLMSG_PUT() */
+ pr_debug("xt_quota2: error during NLMSG_PUT\n");
+}
+#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 int quota_proc_read(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
@@ -226,6 +302,14 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par)
e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len;
ret = !ret;
} else {
+ /* We are transitioning, log that fact. */
+ if (e->quota) {
+ quota2_log(par->hooknum,
+ skb,
+ par->in,
+ par->out,
+ q->name);
+ }
/* we do not allow even small packets from now on */
e->quota = 0;
}
@@ -262,6 +346,14 @@ 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, 1, NULL,
+ NULL, THIS_MODULE);
+ if (!nflognl)
+ return -ENOMEM;
+#endif
+
proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net);
if (proc_xt_quota == NULL)
return -EACCES;