aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter/xt_qtaguid.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/netfilter/xt_qtaguid.c')
-rw-r--r--net/netfilter/xt_qtaguid.c285
1 files changed, 238 insertions, 47 deletions
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index e04635b..e4a14f6 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -26,6 +26,10 @@
#include <net/tcp.h>
#include <net/udp.h>
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#endif
+
#include <linux/netfilter/xt_socket.h>
#include "xt_qtaguid_internal.h"
#include "xt_qtaguid_print.h"
@@ -110,8 +114,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR);
/*---------------------------------------------------------------------------*/
static const char *iface_stat_procdirname = "iface_stat";
static struct proc_dir_entry *iface_stat_procdir;
+/*
+ * The iface_stat_all* will go away once userspace gets use to the new fields
+ * that have a format line.
+ */
static const char *iface_stat_all_procfilename = "iface_stat_all";
static struct proc_dir_entry *iface_stat_all_procfile;
+static const char *iface_stat_fmt_procfilename = "iface_stat_fmt";
+static struct proc_dir_entry *iface_stat_fmt_procfile;
+
/*
* Ordering of locks:
@@ -124,9 +135,9 @@ static struct proc_dir_entry *iface_stat_all_procfile;
* Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock
* is acquired.
*
- * Call tree with all lock holders as of 2011-09-25:
+ * Call tree with all lock holders as of 2012-04-27:
*
- * iface_stat_all_proc_read()
+ * iface_stat_fmt_proc_read()
* iface_stat_list_lock
* (struct iface_stat)
*
@@ -777,13 +788,14 @@ done:
return iface_entry;
}
-static int iface_stat_all_proc_read(char *page, char **num_items_returned,
+static int iface_stat_fmt_proc_read(char *page, char **num_items_returned,
off_t items_to_skip, int char_count,
int *eof, void *data)
{
char *outp = page;
int item_index = 0;
int len;
+ int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */
struct iface_stat *iface_entry;
struct rtnl_link_stats64 dev_stats, *stats;
struct rtnl_link_stats64 no_dev_stats = {0};
@@ -793,14 +805,32 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned,
return 0;
}
- CT_DEBUG("qtaguid:proc iface_stat_all "
+ CT_DEBUG("qtaguid:proc iface_stat_fmt "
+ "pid=%u tgid=%u uid=%u "
"page=%p *num_items_returned=%p off=%ld "
- "char_count=%d *eof=%d\n", page, *num_items_returned,
+ "char_count=%d *eof=%d\n",
+ current->pid, current->tgid, current_fsuid(),
+ page, *num_items_returned,
items_to_skip, char_count, *eof);
if (*eof)
return 0;
+ if (fmt == 2 && item_index++ >= items_to_skip) {
+ len = snprintf(outp, char_count,
+ "ifname "
+ "total_skb_rx_bytes total_skb_rx_packets "
+ "total_skb_tx_bytes total_skb_tx_packets\n"
+ );
+ if (len >= char_count) {
+ *outp = '\0';
+ return outp - page;
+ }
+ outp += len;
+ char_count -= len;
+ (*num_items_returned)++;
+ }
+
/*
* This lock will prevent iface_stat_update() from changing active,
* and in turn prevent an interface from unregistering itself.
@@ -816,18 +846,37 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned,
} else {
stats = &no_dev_stats;
}
- len = snprintf(outp, char_count,
- "%s %d "
- "%llu %llu %llu %llu "
- "%llu %llu %llu %llu\n",
- iface_entry->ifname,
- iface_entry->active,
- iface_entry->totals[IFS_RX].bytes,
- iface_entry->totals[IFS_RX].packets,
- iface_entry->totals[IFS_TX].bytes,
- iface_entry->totals[IFS_TX].packets,
- stats->rx_bytes, stats->rx_packets,
- stats->tx_bytes, stats->tx_packets);
+ /*
+ * If the meaning of the data changes, then update the fmtX
+ * string.
+ */
+ if (fmt == 1) {
+ len = snprintf(
+ outp, char_count,
+ "%s %d "
+ "%llu %llu %llu %llu "
+ "%llu %llu %llu %llu\n",
+ iface_entry->ifname,
+ iface_entry->active,
+ iface_entry->totals_via_dev[IFS_RX].bytes,
+ iface_entry->totals_via_dev[IFS_RX].packets,
+ iface_entry->totals_via_dev[IFS_TX].bytes,
+ iface_entry->totals_via_dev[IFS_TX].packets,
+ stats->rx_bytes, stats->rx_packets,
+ stats->tx_bytes, stats->tx_packets
+ );
+ } else {
+ len = snprintf(
+ outp, char_count,
+ "%s "
+ "%llu %llu %llu %llu\n",
+ iface_entry->ifname,
+ iface_entry->totals_via_skb[IFS_RX].bytes,
+ iface_entry->totals_via_skb[IFS_RX].packets,
+ iface_entry->totals_via_skb[IFS_TX].bytes,
+ iface_entry->totals_via_skb[IFS_TX].packets
+ );
+ }
if (len >= char_count) {
spin_unlock_bh(&iface_stat_list_lock);
*outp = '\0';
@@ -861,13 +910,17 @@ static void iface_create_proc_worker(struct work_struct *work)
new_iface->proc_ptr = proc_entry;
create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->totals[IFS_TX].bytes);
+ read_proc_u64,
+ &new_iface->totals_via_dev[IFS_TX].bytes);
create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->totals[IFS_RX].bytes);
+ read_proc_u64,
+ &new_iface->totals_via_dev[IFS_RX].bytes);
create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->totals[IFS_TX].packets);
+ read_proc_u64,
+ &new_iface->totals_via_dev[IFS_TX].packets);
create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->totals[IFS_RX].packets);
+ read_proc_u64,
+ &new_iface->totals_via_dev[IFS_RX].packets);
create_proc_read_entry("active", proc_iface_perms, proc_entry,
read_proc_bool, &new_iface->active);
@@ -971,11 +1024,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev,
"iface reset its stats unexpectedly\n", __func__,
net_dev->name);
- iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes;
- iface->totals[IFS_TX].packets +=
+ iface->totals_via_dev[IFS_TX].bytes +=
+ iface->last_known[IFS_TX].bytes;
+ iface->totals_via_dev[IFS_TX].packets +=
iface->last_known[IFS_TX].packets;
- iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes;
- iface->totals[IFS_RX].packets +=
+ iface->totals_via_dev[IFS_RX].bytes +=
+ iface->last_known[IFS_RX].bytes;
+ iface->totals_via_dev[IFS_RX].packets +=
iface->last_known[IFS_RX].packets;
iface->last_known_valid = false;
IF_DEBUG("qtaguid: %s(%s): iface=%p "
@@ -1143,6 +1198,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk)
return sock_tag_entry;
}
+static int ipx_proto(const struct sk_buff *skb,
+ struct xt_action_param *par)
+{
+ int thoff, tproto;
+
+ switch (par->family) {
+ case NFPROTO_IPV6:
+ tproto = ipv6_find_hdr(skb, &thoff, -1, NULL);
+ if (tproto < 0)
+ MT_DEBUG("%s(): transport header not found in ipv6"
+ " skb=%p\n", __func__, skb);
+ break;
+ case NFPROTO_IPV4:
+ tproto = ip_hdr(skb)->protocol;
+ break;
+ default:
+ tproto = IPPROTO_RAW;
+ }
+ return tproto;
+}
+
static void
data_counters_update(struct data_counters *dc, int set,
enum ifs_tx_rx direction, int proto, int bytes)
@@ -1203,10 +1279,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only)
spin_unlock_bh(&iface_stat_list_lock);
return;
}
- entry->totals[IFS_TX].bytes += stats->tx_bytes;
- entry->totals[IFS_TX].packets += stats->tx_packets;
- entry->totals[IFS_RX].bytes += stats->rx_bytes;
- entry->totals[IFS_RX].packets += stats->rx_packets;
+ entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes;
+ entry->totals_via_dev[IFS_TX].packets += stats->tx_packets;
+ entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes;
+ entry->totals_via_dev[IFS_RX].packets += stats->rx_packets;
/* We don't need the last_known[] anymore */
entry->last_known_valid = false;
_iface_stat_set_active(entry, net_dev, false);
@@ -1216,6 +1292,67 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only)
spin_unlock_bh(&iface_stat_list_lock);
}
+/*
+ * Update stats for the specified interface from the skb.
+ * Do nothing if the entry
+ * does not exist (when a device was never configured with an IP address).
+ * Called on each sk.
+ */
+static void iface_stat_update_from_skb(const struct sk_buff *skb,
+ struct xt_action_param *par)
+{
+ struct iface_stat *entry;
+ const struct net_device *el_dev;
+ enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
+ int bytes = skb->len;
+
+ if (!skb->dev) {
+ MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
+ el_dev = par->in ? : par->out;
+ } else {
+ const struct net_device *other_dev;
+ el_dev = skb->dev;
+ other_dev = par->in ? : par->out;
+ if (el_dev != other_dev) {
+ MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
+ "par->(in/out)=%p %s\n",
+ par->hooknum, el_dev, el_dev->name, other_dev,
+ other_dev->name);
+ }
+ }
+
+ if (unlikely(!el_dev)) {
+ pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ } else if (unlikely(!el_dev->name)) {
+ pr_err("qtaguid[%d]: %s(): no dev->name?!!\n",
+ par->hooknum, __func__);
+ BUG();
+ } else {
+ int proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto);
+ }
+
+ spin_lock_bh(&iface_stat_list_lock);
+ entry = get_iface_entry(el_dev->name);
+ if (entry == NULL) {
+ IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
+ __func__, el_dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+
+ IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+ el_dev->name, entry);
+
+ entry->totals_via_skb[direction].bytes += bytes;
+ entry->totals_via_skb[direction].packets++;
+ spin_unlock_bh(&iface_stat_list_lock);
+}
+
static void tag_stat_update(struct tag_stat *tag_entry,
enum ifs_tx_rx direction, int proto, int bytes)
{
@@ -1330,8 +1467,19 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
}
if (acct_tag) {
+ /* Create the child {acct_tag, uid_tag} and hook up parent. */
new_tag_stat = create_if_tag_stat(iface_entry, tag);
new_tag_stat->parent_counters = uid_tag_counters;
+ } else {
+ /*
+ * For new_tag_stat to be still NULL here would require:
+ * {0, uid_tag} exists
+ * and {acct_tag, uid_tag} doesn't exist
+ * AND acct_tag == 0.
+ * Impossible. This reassures us that new_tag_stat
+ * below will always be assigned.
+ */
+ BUG_ON(!new_tag_stat);
}
if (new_tag_stat)
@@ -1457,18 +1605,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
parent_procdir);
if (!iface_stat_all_procfile) {
pr_err("qtaguid: iface_stat: init "
- " failed to create stat_all proc entry\n");
+ " failed to create stat_old proc entry\n");
err = -1;
goto err_zap_entry;
}
- iface_stat_all_procfile->read_proc = iface_stat_all_proc_read;
+ iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read;
+ iface_stat_all_procfile->data = (void *)1; /* fmt1 */
+
+ iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename,
+ proc_iface_perms,
+ parent_procdir);
+ if (!iface_stat_fmt_procfile) {
+ pr_err("qtaguid: iface_stat: init "
+ " failed to create stat_all proc entry\n");
+ err = -1;
+ goto err_zap_all_stats_entry;
+ }
+ iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read;
+ iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */
err = register_netdevice_notifier(&iface_netdev_notifier_blk);
if (err) {
pr_err("qtaguid: iface_stat: init "
"failed to register dev event handler\n");
- goto err_zap_all_stats_entry;
+ goto err_zap_all_stats_entries;
}
err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk);
if (err) {
@@ -1492,6 +1653,8 @@ err_unreg_ip4_addr:
unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk);
err_unreg_nd:
unregister_netdevice_notifier(&iface_netdev_notifier_blk);
+err_zap_all_stats_entries:
+ remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir);
err_zap_all_stats_entry:
remove_proc_entry(iface_stat_all_procfilename, parent_procdir);
err_zap_entry:
@@ -1571,15 +1734,15 @@ static void account_for_uid(const struct sk_buff *skb,
} else if (unlikely(!el_dev->name)) {
pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum);
} else {
- MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n",
- par->hooknum,
- el_dev->name,
- el_dev->type);
+ int proto = ipx_proto(skb, par);
+ MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
+ par->hooknum, el_dev->name, el_dev->type,
+ par->family, proto);
if_tag_stat_update(el_dev->name, uid,
skb->sk ? skb->sk : alternate_sk,
par->in ? IFS_RX : IFS_TX,
- ip_hdr(skb)->protocol, skb->len);
+ proto, skb->len);
}
}
@@ -1604,8 +1767,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
goto ret_res;
}
- sk = skb->sk;
+ switch (par->hooknum) {
+ case NF_INET_PRE_ROUTING:
+ case NF_INET_POST_ROUTING:
+ atomic64_inc(&qtu_events.match_calls_prepost);
+ iface_stat_update_from_skb(skb, par);
+ /*
+ * We are done in pre/post. The skb will get processed
+ * further alter.
+ */
+ res = (info->match ^ info->invert);
+ goto ret_res;
+ break;
+ /* default: Fall through and do UID releated work */
+ }
+ sk = skb->sk;
if (sk == NULL) {
/*
* A missing sk->sk_socket happens when packets are in-flight
@@ -1624,8 +1801,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
} else {
atomic64_inc(&qtu_events.match_found_sk);
}
- MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n",
- par->hooknum, sk, got_sock, ip_hdr(skb)->protocol);
+ MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
+ par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
if (sk != NULL) {
MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
par->hooknum, sk, sk->sk_socket,
@@ -1780,8 +1957,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
if (*eof)
return 0;
- CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n",
- page, items_to_skip, char_count, *eof);
+ CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u "
+ "page=%p off=%ld char_count=%d *eof=%d\n",
+ current->pid, current->tgid, current_fsuid(),
+ page, items_to_skip, char_count, *eof);
spin_lock_bh(&sock_tag_list_lock);
for (node = rb_first(&sock_tag_tree);
@@ -1825,6 +2004,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
"delete_cmds=%llu "
"iface_events=%llu "
"match_calls=%llu "
+ "match_calls_prepost=%llu "
"match_found_sk=%llu "
"match_found_sk_in_ct=%llu "
"match_found_no_sk_in_ct=%llu "
@@ -1836,6 +2016,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
atomic64_read(&qtu_events.delete_cmds),
atomic64_read(&qtu_events.iface_events),
atomic64_read(&qtu_events.match_calls),
+ atomic64_read(&qtu_events.match_calls_prepost),
atomic64_read(&qtu_events.match_found_sk),
atomic64_read(&qtu_events.match_found_sk_in_ct),
atomic64_read(
@@ -2109,7 +2290,9 @@ static int ctrl_cmd_tag(const char *input)
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
if (!el_socket) {
pr_info("qtaguid: ctrl_tag(%s): failed to lookup"
- " sock_fd=%d err=%d\n", input, sock_fd, res);
+ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
+ input, sock_fd, res, current->pid, current->tgid,
+ current_fsuid());
goto err;
}
CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2254,7 +2437,9 @@ static int ctrl_cmd_untag(const char *input)
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
if (!el_socket) {
pr_info("qtaguid: ctrl_untag(%s): failed to lookup"
- " sock_fd=%d err=%d\n", input, sock_fd, res);
+ " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
+ input, sock_fd, res, current->pid, current->tgid,
+ current_fsuid());
goto err;
}
CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2330,6 +2515,9 @@ static int qtaguid_ctrl_parse(const char *input, int count)
char cmd;
int res;
+ CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n",
+ input, current->pid, current->tgid, current_fsuid());
+
cmd = input[0];
/* Collect params for commands */
switch (cmd) {
@@ -2506,9 +2694,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
return len;
}
- CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld "
- "char_count=%d *eof=%d\n", page, *num_items_returned,
- items_to_skip, char_count, *eof);
+ CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u "
+ "page=%p *num_items_returned=%p off=%ld "
+ "char_count=%d *eof=%d\n",
+ current->pid, current->tgid, current_fsuid(),
+ page, *num_items_returned,
+ items_to_skip, char_count, *eof);
if (*eof)
return 0;