netfilter: xt_qtaguid: provide an iface_stat_all proc entry
JP Abgrall [Tue, 20 Sep 2011 05:54:51 +0000 (22:54 -0700)]
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>

net/netfilter/xt_qtaguid.c
net/netfilter/xt_qtaguid_internal.h
net/netfilter/xt_qtaguid_print.c

index 3cdf1d8..9b9b809 100644 (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:
index f762704..d39ee89 100644 (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
index 7fef3a3..45d717f 100644 (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);
 }