netfilter: xt_qtaguid: provide an iface_stat_all proc entry

There is a
  /proc/net/xt_qtaguid/iface/<iface>/{rx_bytes,rx_packets,tx_bytes,...}
but for better convenience and to avoid getting overly stale net/dev stats
we now have
  /proc/net/xt_qtaguid/iface_stat_all
which outputs lines of:
  iface_name active rx_bytes rx_packets tx_bytes tx_packets
    net_dev_rx_bytes net_dev_rx_packets net_dev_tx_bytes net_dev_tx_packets

Change-Id: I12cc10d2d123b86b56d4eb489b1d77b2ce72ebcf
Signed-off-by: JP Abgrall <jpa@google.com>
This commit is contained in:
JP Abgrall
2011-09-19 22:54:51 -07:00
parent 5ea3a7c6c6
commit 1d1fa838dd
3 changed files with 151 additions and 49 deletions

View File

@@ -111,6 +111,8 @@ module_param(debug_mask, uint, S_IRUGO | S_IWUSR);
/*---------------------------------------------------------------------------*/
static const char *iface_stat_procdirname = "iface_stat";
static struct proc_dir_entry *iface_stat_procdir;
static const char *iface_stat_all_procfilename = "iface_stat_all";
static struct proc_dir_entry *iface_stat_all_procfile;
/*
* Ordering of locks:
@@ -751,6 +753,72 @@ done:
return iface_entry;
}
static int iface_stat_all_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;
struct iface_stat *iface_entry;
struct rtnl_link_stats64 dev_stats, *stats;
struct rtnl_link_stats64 no_dev_stats = {0};
if (unlikely(module_passive)) {
*eof = 1;
return 0;
}
CT_DEBUG("qtaguid:proc iface_stat_all "
"page=%p *num_items_returned=%p off=%ld "
"char_count=%d *eof=%d\n", page, *num_items_returned,
items_to_skip, char_count, *eof);
if (*eof)
return 0;
/*
* This lock will prevent iface_stat_update() from changing active,
* and in turn prevent an interface from unregistering itself.
*/
spin_lock_bh(&iface_stat_list_lock);
list_for_each_entry(iface_entry, &iface_stat_list, list) {
if (item_index++ < items_to_skip)
continue;
if (iface_entry->active) {
stats = dev_get_stats(iface_entry->net_dev,
&dev_stats);
} 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 (len >= char_count) {
spin_unlock_bh(&iface_stat_list_lock);
*outp = '\0';
return outp - page;
}
outp += len;
char_count -= len;
(*num_items_returned)++;
}
spin_unlock_bh(&iface_stat_list_lock);
*eof = 1;
return outp - page;
}
static void iface_create_proc_worker(struct work_struct *work)
{
struct proc_dir_entry *proc_entry;
@@ -784,8 +852,34 @@ static void iface_create_proc_worker(struct work_struct *work)
kfree(isw);
}
/*
* Will set the entry's active state, and
* update the net_dev accordingly also.
*/
static void _iface_stat_set_active(struct iface_stat *entry,
struct net_device *net_dev,
bool activate)
{
if (activate) {
entry->net_dev = net_dev;
entry->active = true;
IF_DEBUG("qtaguid: %s(%s): "
"enable tracking. rfcnt=%d\n", __func__,
entry->ifname,
percpu_read(*net_dev->pcpu_refcnt));
} else {
entry->active = false;
entry->net_dev = NULL;
IF_DEBUG("qtaguid: %s(%s): "
"disable tracking. rfcnt=%d\n", __func__,
entry->ifname,
percpu_read(*net_dev->pcpu_refcnt));
}
}
/* Caller must hold iface_stat_list_lock */
static struct iface_stat *iface_alloc(const char *ifname)
static struct iface_stat *iface_alloc(struct net_device *net_dev)
{
struct iface_stat *new_iface;
struct iface_stat_work *isw;
@@ -793,19 +887,19 @@ static struct iface_stat *iface_alloc(const char *ifname)
new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC);
if (new_iface == NULL) {
pr_err("qtaguid: iface_stat: create(%s): "
"iface_stat alloc failed\n", ifname);
"iface_stat alloc failed\n", net_dev->name);
return NULL;
}
new_iface->ifname = kstrdup(ifname, GFP_ATOMIC);
new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC);
if (new_iface->ifname == NULL) {
pr_err("qtaguid: iface_stat: create(%s): "
"ifname alloc failed\n", ifname);
"ifname alloc failed\n", net_dev->name);
kfree(new_iface);
return NULL;
}
spin_lock_init(&new_iface->tag_stat_list_lock);
new_iface->active = true;
new_iface->tag_stat_tree = RB_ROOT;
_iface_stat_set_active(new_iface, net_dev, true);
/*
* ipv6 notifier chains are atomic :(
@@ -815,6 +909,7 @@ static struct iface_stat *iface_alloc(const char *ifname)
if (!isw) {
pr_err("qtaguid: iface_stat: create(%s): "
"work alloc failed\n", new_iface->ifname);
_iface_stat_set_active(new_iface, net_dev, false);
kfree(new_iface->ifname);
kfree(new_iface);
return NULL;
@@ -918,20 +1013,14 @@ static void iface_stat_create(struct net_device *net_dev,
spin_lock_bh(&iface_stat_list_lock);
entry = get_iface_entry(ifname);
if (entry != NULL) {
bool activate = !ipv4_is_loopback(ipaddr);
IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n",
ifname, entry);
iface_check_stats_reset_and_adjust(net_dev, entry);
if (ipv4_is_loopback(ipaddr)) {
entry->active = false;
IF_DEBUG("qtaguid: iface_stat: create(%s): "
"disable tracking of loopback dev\n",
ifname);
} else {
entry->active = true;
IF_DEBUG("qtaguid: iface_stat: create(%s): "
"enable tracking. ip=%pI4\n",
ifname, &ipaddr);
}
_iface_stat_set_active(entry, net_dev, activate);
IF_DEBUG("qtaguid: %s(%s): "
"tracking now %d on ip=%pI4\n", __func__,
entry->ifname, activate, &ipaddr);
goto done_unlock_put;
} else if (ipv4_is_loopback(ipaddr)) {
IF_DEBUG("qtaguid: iface_stat: create(%s): "
@@ -939,10 +1028,9 @@ static void iface_stat_create(struct net_device *net_dev,
goto done_unlock_put;
}
new_iface = iface_alloc(ifname);
new_iface = iface_alloc(net_dev);
IF_DEBUG("qtaguid: iface_stat: create(%s): done "
"entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr);
done_unlock_put:
spin_unlock_bh(&iface_stat_list_lock);
done_put:
@@ -987,29 +1075,23 @@ static void iface_stat_create_ipv6(struct net_device *net_dev,
spin_lock_bh(&iface_stat_list_lock);
entry = get_iface_entry(ifname);
if (entry != NULL) {
IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n",
bool activate = !(addr_type & IPV6_ADDR_LOOPBACK);
IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
ifname, entry);
iface_check_stats_reset_and_adjust(net_dev, entry);
if (addr_type & IPV6_ADDR_LOOPBACK) {
entry->active = false;
IF_DEBUG("qtaguid: iface_stat: create6(%s): "
"disable tracking of loopback dev\n",
ifname);
} else {
entry->active = true;
IF_DEBUG("qtaguid: iface_stat: create6(%s): "
"enable tracking. ip=%pI6c\n",
ifname, &ifa->addr);
}
_iface_stat_set_active(entry, net_dev, activate);
IF_DEBUG("qtaguid: %s(%s): "
"tracking now %d on ip=%pI6c\n", __func__,
entry->ifname, activate, &ifa->addr);
goto done_unlock_put;
} else if (addr_type & IPV6_ADDR_LOOPBACK) {
IF_DEBUG("qtaguid: iface_stat: create6(%s): "
"ignore loopback dev. ip=%pI6c\n",
IF_DEBUG("qtaguid: %s(%s): "
"ignore loopback dev. ip=%pI6c\n", __func__,
ifname, &ifa->addr);
goto done_unlock_put;
}
new_iface = iface_alloc(ifname);
new_iface = iface_alloc(net_dev);
IF_DEBUG("qtaguid: iface_stat: create6(%s): done "
"entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr);
@@ -1061,26 +1143,26 @@ data_counters_update(struct data_counters *dc, int set,
* does not exist (when a device was never configured with an IP address).
* Called when an device is being unregistered.
*/
static void iface_stat_update(struct net_device *dev, bool stash_only)
static void iface_stat_update(struct net_device *net_dev, bool stash_only)
{
struct rtnl_link_stats64 dev_stats, *stats;
struct iface_stat *entry;
stats = dev_get_stats(dev, &dev_stats);
stats = dev_get_stats(net_dev, &dev_stats);
spin_lock_bh(&iface_stat_list_lock);
entry = get_iface_entry(dev->name);
entry = get_iface_entry(net_dev->name);
if (entry == NULL) {
IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n",
dev->name);
net_dev->name);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
IF_DEBUG("qtaguid: iface_stat: update(%s): entry=%p\n",
dev->name, entry);
IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
net_dev->name, entry);
if (!entry->active) {
IF_DEBUG("qtaguid: iface_stat: update(%s): already disabled\n",
dev->name);
IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__,
net_dev->name);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
@@ -1091,9 +1173,9 @@ static void iface_stat_update(struct net_device *dev, bool stash_only)
entry->last_known[IFS_RX].bytes = stats->rx_bytes;
entry->last_known[IFS_RX].packets = stats->rx_packets;
entry->last_known_valid = true;
IF_DEBUG("qtaguid: iface_stat: update(%s): "
"dev stats stashed rx/tx=%llu/%llu\n",
dev->name, stats->rx_bytes, stats->tx_bytes);
IF_DEBUG("qtaguid: %s(%s): "
"dev stats stashed rx/tx=%llu/%llu\n", __func__,
net_dev->name, stats->rx_bytes, stats->tx_bytes);
spin_unlock_bh(&iface_stat_list_lock);
return;
}
@@ -1103,10 +1185,10 @@ static void iface_stat_update(struct net_device *dev, bool stash_only)
entry->totals[IFS_RX].packets += stats->rx_packets;
/* We don't need the last_known[] anymore */
entry->last_known_valid = false;
entry->active = false;
IF_DEBUG("qtaguid: iface_stat: update(%s): "
"disable tracking. rx/tx=%llu/%llu\n",
dev->name, stats->rx_bytes, stats->tx_bytes);
_iface_stat_set_active(entry, net_dev, false);
IF_DEBUG("qtaguid: %s(%s): "
"disable tracking. rx/tx=%llu/%llu\n", __func__,
net_dev->name, stats->rx_bytes, stats->tx_bytes);
spin_unlock_bh(&iface_stat_list_lock);
}
@@ -1340,11 +1422,24 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
err = -1;
goto err;
}
iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename,
proc_iface_perms,
parent_procdir);
if (!iface_stat_all_procfile) {
pr_err("qtaguid: iface_stat: init "
" failed to create stat_all proc entry\n");
err = -1;
goto err_zap_entry;
}
iface_stat_all_procfile->read_proc = iface_stat_all_proc_read;
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_entry;
goto err_zap_all_stats_entry;
}
err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk);
if (err) {
@@ -1365,6 +1460,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_entry:
remove_proc_entry(iface_stat_all_procfilename, parent_procdir);
err_zap_entry:
remove_proc_entry(iface_stat_procdirname, parent_procdir);
err:

View File

@@ -197,6 +197,9 @@ struct iface_stat {
struct list_head list; /* in iface_stat_list */
char *ifname;
bool active;
/* net_dev is only valid for active iface_stat */
struct net_device *net_dev;
struct byte_packet_counters totals[IFS_MAX_DIRECTIONS];
/*
* We keep the last_known, because some devices reset their counters

View File

@@ -156,6 +156,7 @@ char *pp_iface_stat(struct iface_stat *is)
"tx={bytes=%llu, "
"packets=%llu}}, "
"active=%d, "
"net_dev=%p, "
"proc_ptr=%p, "
"tag_stat_tree=rb_root{...}}",
is,
@@ -170,6 +171,7 @@ char *pp_iface_stat(struct iface_stat *is)
is->last_known[IFS_TX].bytes,
is->last_known[IFS_TX].packets,
is->active,
is->net_dev,
is->proc_ptr);
}