Revert "netfilter: xt_qtaguid: fix crash on non-full sks"
[firefly-linux-kernel-4.4.55.git] / net / netfilter / xt_qtaguid.c
index 992a6e044902a8e5ef9eb9459b3a67e5b96db9ad..e1442bfb668dbaf9ecd3f7f425cc70a49b500a1a 100644 (file)
 #include <linux/file.h>
 #include <linux/inetdevice.h>
 #include <linux/module.h>
+#include <linux/miscdevice.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_qtaguid.h>
+#include <linux/ratelimit.h>
+#include <linux/seq_file.h>
 #include <linux/skbuff.h>
 #include <linux/workqueue.h>
 #include <net/addrconf.h>
@@ -33,6 +36,7 @@
 #include <linux/netfilter/xt_socket.h>
 #include "xt_qtaguid_internal.h"
 #include "xt_qtaguid_print.h"
+#include "../../fs/proc/internal.h"
 
 /*
  * We only use the xt_socket funcs within a similar context to avoid unexpected
@@ -121,104 +125,6 @@ static const char *iface_stat_fmt_procfilename = "iface_stat_fmt";
 static struct proc_dir_entry *iface_stat_fmt_procfile;
 
 
-/*
- * Ordering of locks:
- *  outer locks:
- *    iface_stat_list_lock
- *    sock_tag_list_lock
- *  inner locks:
- *    uid_tag_data_tree_lock
- *    tag_counter_set_list_lock
- * 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 2012-04-27:
- *
- * iface_stat_fmt_proc_read()
- *   iface_stat_list_lock
- *     (struct iface_stat)
- *
- * qtaguid_ctrl_proc_read()
- *   sock_tag_list_lock
- *     (sock_tag_tree)
- *     (struct proc_qtu_data->sock_tag_list)
- *   prdebug_full_state()
- *     sock_tag_list_lock
- *       (sock_tag_tree)
- *     uid_tag_data_tree_lock
- *       (uid_tag_data_tree)
- *       (proc_qtu_data_tree)
- *     iface_stat_list_lock
- *
- * qtaguid_stats_proc_read()
- *   iface_stat_list_lock
- *     struct iface_stat->tag_stat_list_lock
- *
- * qtudev_open()
- *   uid_tag_data_tree_lock
- *
- * qtudev_release()
- *   sock_tag_data_list_lock
- *     uid_tag_data_tree_lock
- *   prdebug_full_state()
- *     sock_tag_list_lock
- *     uid_tag_data_tree_lock
- *     iface_stat_list_lock
- *
- * iface_netdev_event_handler()
- *   iface_stat_create()
- *     iface_stat_list_lock
- *   iface_stat_update()
- *     iface_stat_list_lock
- *
- * iface_inetaddr_event_handler()
- *   iface_stat_create()
- *     iface_stat_list_lock
- *   iface_stat_update()
- *     iface_stat_list_lock
- *
- * iface_inet6addr_event_handler()
- *   iface_stat_create_ipv6()
- *     iface_stat_list_lock
- *   iface_stat_update()
- *     iface_stat_list_lock
- *
- * qtaguid_mt()
- *   account_for_uid()
- *     if_tag_stat_update()
- *       get_sock_stat()
- *         sock_tag_list_lock
- *       struct iface_stat->tag_stat_list_lock
- *         tag_stat_update()
- *           get_active_counter_set()
- *             tag_counter_set_list_lock
- *         tag_stat_update()
- *           get_active_counter_set()
- *             tag_counter_set_list_lock
- *
- *
- * qtaguid_ctrl_parse()
- *   ctrl_cmd_delete()
- *     sock_tag_list_lock
- *     tag_counter_set_list_lock
- *     iface_stat_list_lock
- *       struct iface_stat->tag_stat_list_lock
- *     uid_tag_data_tree_lock
- *   ctrl_cmd_counter_set()
- *     tag_counter_set_list_lock
- *   ctrl_cmd_tag()
- *     sock_tag_list_lock
- *       (sock_tag_tree)
- *       get_tag_ref()
- *         uid_tag_data_tree_lock
- *           (uid_tag_data_tree)
- *       uid_tag_data_tree_lock
- *         (proc_qtu_data_tree)
- *   ctrl_cmd_untag()
- *     sock_tag_list_lock
- *     uid_tag_data_tree_lock
- *
- */
 static LIST_HEAD(iface_stat_list);
 static DEFINE_SPINLOCK(iface_stat_list_lock);
 
@@ -240,22 +146,22 @@ static bool can_manipulate_uids(void)
 {
        /* root pwnd */
        return in_egroup_p(xt_qtaguid_ctrl_file->gid)
-               || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited)
-               || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid);
+               || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited)
+               || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
 }
 
-static bool can_impersonate_uid(uid_t uid)
+static bool can_impersonate_uid(kuid_t uid)
 {
-       return uid == current_fsuid() || can_manipulate_uids();
+       return uid_eq(uid, current_fsuid()) || can_manipulate_uids();
 }
 
-static bool can_read_other_uid_stats(uid_t uid)
+static bool can_read_other_uid_stats(kuid_t uid)
 {
        /* root pwnd */
        return in_egroup_p(xt_qtaguid_stats_file->gid)
-               || unlikely(!current_fsuid()) || uid == current_fsuid()
+               || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid())
                || unlikely(!proc_stats_readall_limited)
-               || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid);
+               || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
 }
 
 static inline void dc_add_byte_packets(struct data_counters *counters, int set,
@@ -637,7 +543,7 @@ static void put_utd_entry(struct uid_tag_data *utd_entry)
                         "erase utd_entry=%p uid=%u "
                         "by pid=%u tgid=%u uid=%u\n", __func__,
                         utd_entry, utd_entry->uid,
-                        current->pid, current->tgid, current_fsuid());
+                        current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                BUG_ON(utd_entry->num_active_tags);
                rb_erase(&utd_entry->node, &uid_tag_data_tree);
                kfree(utd_entry);
@@ -689,42 +595,26 @@ static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry)
        }
 }
 
-static int read_proc_u64(char *page, char **start, off_t off,
-                       int count, int *eof, void *data)
+static ssize_t read_proc_u64(struct file *file, char __user *buf,
+                        size_t size, loff_t *ppos)
 {
-       int len;
-       uint64_t value;
-       char *p = page;
-       uint64_t *iface_entry = data;
-
-       if (!data)
-               return 0;
+       uint64_t *valuep = PDE_DATA(file_inode(file));
+       char tmp[24];
+       size_t tmp_size;
 
-       value = *iface_entry;
-       p += sprintf(p, "%llu\n", value);
-       len = (p - page) - off;
-       *eof = (len <= count) ? 1 : 0;
-       *start = page + off;
-       return len;
+       tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep);
+       return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
 }
 
-static int read_proc_bool(char *page, char **start, off_t off,
-                       int count, int *eof, void *data)
+static ssize_t read_proc_bool(struct file *file, char __user *buf,
+                         size_t size, loff_t *ppos)
 {
-       int len;
-       bool value;
-       char *p = page;
-       bool *bool_entry = data;
-
-       if (!data)
-               return 0;
+       bool *valuep = PDE_DATA(file_inode(file));
+       char tmp[24];
+       size_t tmp_size;
 
-       value = *bool_entry;
-       p += sprintf(p, "%u\n", value);
-       len = (p - page) - off;
-       *eof = (len <= count) ? 1 : 0;
-       *start = page + off;
-       return len;
+       tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep);
+       return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
 }
 
 static int get_active_counter_set(tag_t tag)
@@ -770,144 +660,132 @@ done:
 }
 
 /* This is for fmt2 only */
-static int pp_iface_stat_line(bool header, char *outp,
-                             int char_count, struct iface_stat *iface_entry)
-{
-       int len;
-       if (header) {
-               len = snprintf(outp, char_count,
-                              "ifname "
-                              "total_skb_rx_bytes total_skb_rx_packets "
-                              "total_skb_tx_bytes total_skb_tx_packets "
-                              "rx_tcp_bytes rx_tcp_packets "
-                              "rx_udp_bytes rx_udp_packets "
-                              "rx_other_bytes rx_other_packets "
-                              "tx_tcp_bytes tx_tcp_packets "
-                              "tx_udp_bytes tx_udp_packets "
-                              "tx_other_bytes tx_other_packets\n"
-                       );
-       } else {
-               struct data_counters *cnts;
-               int cnt_set = 0;   /* We only use one set for the device */
-               cnts = &iface_entry->totals_via_skb;
-               len = snprintf(
-                       outp, char_count,
-                       "%s "
-                       "%llu %llu %llu %llu %llu %llu %llu %llu "
-                       "%llu %llu %llu %llu %llu %llu %llu %llu\n",
-                       iface_entry->ifname,
-                       dc_sum_bytes(cnts, cnt_set, IFS_RX),
-                       dc_sum_packets(cnts, cnt_set, IFS_RX),
-                       dc_sum_bytes(cnts, cnt_set, IFS_TX),
-                       dc_sum_packets(cnts, cnt_set, IFS_TX),
-                       cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
-       }
-       return len;
-}
-
-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};
-
-       if (unlikely(module_passive)) {
-               *eof = 1;
-               return 0;
-       }
-
-       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",
-                current->pid, current->tgid, current_fsuid(),
-                page, *num_items_returned,
-                items_to_skip, char_count, *eof);
+static void pp_iface_stat_header(struct seq_file *m)
+{
+       seq_puts(m,
+                "ifname "
+                "total_skb_rx_bytes total_skb_rx_packets "
+                "total_skb_tx_bytes total_skb_tx_packets "
+                "rx_tcp_bytes rx_tcp_packets "
+                "rx_udp_bytes rx_udp_packets "
+                "rx_other_bytes rx_other_packets "
+                "tx_tcp_bytes tx_tcp_packets "
+                "tx_udp_bytes tx_udp_packets "
+                "tx_other_bytes tx_other_packets\n"
+       );
+}
 
-       if (*eof)
-               return 0;
+static void pp_iface_stat_line(struct seq_file *m,
+                              struct iface_stat *iface_entry)
+{
+       struct data_counters *cnts;
+       int cnt_set = 0;   /* We only use one set for the device */
+       cnts = &iface_entry->totals_via_skb;
+       seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu "
+                  "%llu %llu %llu %llu %llu %llu %llu %llu\n",
+                  iface_entry->ifname,
+                  dc_sum_bytes(cnts, cnt_set, IFS_RX),
+                  dc_sum_packets(cnts, cnt_set, IFS_RX),
+                  dc_sum_bytes(cnts, cnt_set, IFS_TX),
+                  dc_sum_packets(cnts, cnt_set, IFS_TX),
+                  cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
+                  cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
+                  cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
+                  cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
+                  cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
+                  cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
+                  cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
+}
+
+struct proc_iface_stat_fmt_info {
+       int fmt;
+};
 
-       if (fmt == 2 && item_index++ >= items_to_skip) {
-               len = pp_iface_stat_line(true, outp, char_count, NULL);
-               if (len >= char_count) {
-                       *outp = '\0';
-                       return outp - page;
-               }
-               outp += len;
-               char_count -= len;
-               (*num_items_returned)++;
-       }
+static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos)
+{
+       struct proc_iface_stat_fmt_info *p = m->private;
+       loff_t n = *pos;
 
        /*
         * 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;
-               }
-               /*
-                * 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 = pp_iface_stat_line(false, outp, char_count,
-                                                iface_entry);
-               }
-               if (len >= char_count) {
-                       spin_unlock_bh(&iface_stat_list_lock);
-                       *outp = '\0';
-                       return outp - page;
-               }
-               outp += len;
-               char_count -= len;
-               (*num_items_returned)++;
-       }
+       if (unlikely(module_passive))
+               return NULL;
+
+       if (!n && p->fmt == 2)
+               pp_iface_stat_header(m);
+
+       return seq_list_start(&iface_stat_list, n);
+}
+
+static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos)
+{
+       return seq_list_next(p, &iface_stat_list, pos);
+}
+
+static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p)
+{
        spin_unlock_bh(&iface_stat_list_lock);
+}
+
+static int iface_stat_fmt_proc_show(struct seq_file *m, void *v)
+{
+       struct proc_iface_stat_fmt_info *p = m->private;
+       struct iface_stat *iface_entry;
+       struct rtnl_link_stats64 dev_stats, *stats;
+       struct rtnl_link_stats64 no_dev_stats = {0};
+
+
+       CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n",
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
+
+       iface_entry = list_entry(v, struct iface_stat, list);
 
-       *eof = 1;
-       return outp - page;
+       if (iface_entry->active) {
+               stats = dev_get_stats(iface_entry->net_dev,
+                                     &dev_stats);
+       } else {
+               stats = &no_dev_stats;
+       }
+       /*
+        * If the meaning of the data changes, then update the fmtX
+        * string.
+        */
+       if (p->fmt == 1) {
+               seq_printf(m, "%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 {
+               pp_iface_stat_line(m, iface_entry);
+       }
+       return 0;
 }
 
+static const struct file_operations read_u64_fops = {
+       .read           = read_proc_u64,
+       .llseek         = default_llseek,
+};
+
+static const struct file_operations read_bool_fops = {
+       .read           = read_proc_bool,
+       .llseek         = default_llseek,
+};
+
 static void iface_create_proc_worker(struct work_struct *work)
 {
        struct proc_dir_entry *proc_entry;
@@ -925,20 +803,20 @@ 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_via_dev[IFS_TX].bytes);
-       create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry,
-                              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_via_dev[IFS_TX].packets);
-       create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry,
-                              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);
+       proc_create_data("tx_bytes", proc_iface_perms, proc_entry,
+                        &read_u64_fops,
+                        &new_iface->totals_via_dev[IFS_TX].bytes);
+       proc_create_data("rx_bytes", proc_iface_perms, proc_entry,
+                        &read_u64_fops,
+                        &new_iface->totals_via_dev[IFS_RX].bytes);
+       proc_create_data("tx_packets", proc_iface_perms, proc_entry,
+                        &read_u64_fops,
+                        &new_iface->totals_via_dev[IFS_TX].packets);
+       proc_create_data("rx_packets", proc_iface_perms, proc_entry,
+                        &read_u64_fops,
+                        &new_iface->totals_via_dev[IFS_RX].packets);
+       proc_create_data("active", proc_iface_perms, proc_entry,
+                        &read_bool_fops, &new_iface->active);
 
        IF_DEBUG("qtaguid: iface_stat: create_proc(): done "
                 "entry=%p dev=%s\n", new_iface, new_iface->ifname);
@@ -1328,12 +1206,12 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb,
        }
 
        if (unlikely(!el_dev)) {
-               pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
-                      par->hooknum, __func__);
+               pr_err_ratelimited("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__);
+               pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n",
+                                  par->hooknum, __func__);
                BUG();
        } else {
                proto = ipx_proto(skb, par);
@@ -1413,11 +1291,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
                "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n",
                 ifname, uid, sk, direction, proto, bytes);
 
-
+       spin_lock_bh(&iface_stat_list_lock);
        iface_entry = get_iface_entry(ifname);
        if (!iface_entry) {
-               pr_err("qtaguid: iface_stat: stat_update() %s not found\n",
-                      ifname);
+               pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
+                                  "%s not found\n", ifname);
+               spin_unlock_bh(&iface_stat_list_lock);
                return;
        }
        /* It is ok to process data when an iface_entry is inactive */
@@ -1453,8 +1332,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
                 * {0, uid_tag} will also get updated.
                 */
                tag_stat_update(tag_stat_entry, direction, proto, bytes);
-               spin_unlock_bh(&iface_entry->tag_stat_list_lock);
-               return;
+               goto unlock;
        }
 
        /* Loop over tag list under this interface for {0,uid_tag} */
@@ -1494,11 +1372,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
        tag_stat_update(new_tag_stat, direction, proto, bytes);
 unlock:
        spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+       spin_unlock_bh(&iface_stat_list_lock);
 }
 
 static int iface_netdev_event_handler(struct notifier_block *nb,
                                      unsigned long event, void *ptr) {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (unlikely(module_passive))
                return NOTIFY_DONE;
@@ -1595,6 +1474,33 @@ static struct notifier_block iface_inet6addr_notifier_blk = {
        .notifier_call = iface_inet6addr_event_handler,
 };
 
+static const struct seq_operations iface_stat_fmt_proc_seq_ops = {
+       .start  = iface_stat_fmt_proc_start,
+       .next   = iface_stat_fmt_proc_next,
+       .stop   = iface_stat_fmt_proc_stop,
+       .show   = iface_stat_fmt_proc_show,
+};
+
+static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file)
+{
+       struct proc_iface_stat_fmt_info *s;
+
+       s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops,
+                       sizeof(struct proc_iface_stat_fmt_info));
+       if (!s)
+               return -ENOMEM;
+
+       s->fmt = (uintptr_t)PDE_DATA(inode);
+       return 0;
+}
+
+static const struct file_operations proc_iface_stat_fmt_fops = {
+       .open           = proc_iface_stat_fmt_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
 static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
 {
        int err;
@@ -1606,29 +1512,29 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
                goto err;
        }
 
-       iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename,
-                                                   proc_iface_perms,
-                                                   parent_procdir);
+       iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename,
+                                                  proc_iface_perms,
+                                                  parent_procdir,
+                                                  &proc_iface_stat_fmt_fops,
+                                                  (void *)1 /* fmt1 */);
        if (!iface_stat_all_procfile) {
                pr_err("qtaguid: iface_stat: init "
                       " failed to create stat_old proc entry\n");
                err = -1;
                goto err_zap_entry;
        }
-       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);
+       iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename,
+                                                  proc_iface_perms,
+                                                  parent_procdir,
+                                                  &proc_iface_stat_fmt_fops,
+                                                  (void *)2 /* fmt2 */);
        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);
@@ -1684,25 +1590,24 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
 
        switch (par->family) {
        case NFPROTO_IPV6:
-               sk = xt_socket_get6_sk(skb, par);
+               sk = xt_socket_lookup_slow_v6(dev_net(skb->dev), skb, par->in);
                break;
        case NFPROTO_IPV4:
-               sk = xt_socket_get4_sk(skb, par);
+               sk = xt_socket_lookup_slow_v4(dev_net(skb->dev), skb, par->in);
                break;
        default:
                return NULL;
        }
 
-       /*
-        * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs.
-        * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959
-        * Not fixed in 3.0-r3 :(
-        */
        if (sk) {
                MT_DEBUG("qtaguid: %p->sk_proto=%u "
                         "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
+               /*
+                * When in TCP_TIME_WAIT the sk is not a "struct sock" but
+                * "struct inet_timewait_sock" which is missing fields.
+                */
                if (sk->sk_state  == TCP_TIME_WAIT) {
-                       xt_socket_put_sk(sk);
+                       sock_gen_put(sk);
                        sk = NULL;
                }
        }
@@ -1753,8 +1658,9 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
        const struct file *filp;
        bool got_sock = false;
        struct sock *sk;
-       uid_t sock_uid;
+       kuid_t sock_uid;
        bool res;
+       bool set_sk_callback_lock = false;
 
        if (unlikely(module_passive))
                return (info->match ^ info->invert) == 0;
@@ -1784,6 +1690,13 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
        }
 
        sk = skb->sk;
+       /*
+        * When in TCP_TIME_WAIT the sk is not a "struct sock" but
+        * "struct inet_timewait_sock" which is missing fields.
+        * So we ignore it.
+        */
+       if (sk && sk->sk_state == TCP_TIME_WAIT)
+               sk = NULL;
        if (sk == NULL) {
                /*
                 * A missing sk->sk_socket happens when packets are in-flight
@@ -1805,12 +1718,14 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
        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) {
+               set_sk_callback_lock = true;
+               read_lock_bh(&sk->sk_callback_lock);
                MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
                        par->hooknum, sk, sk->sk_socket,
                        sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
                filp = sk->sk_socket ? sk->sk_socket->file : NULL;
                MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
-                       par->hooknum, filp ? filp->f_cred->fsuid : -1);
+                       par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
        }
 
        if (sk == NULL || sk->sk_socket == NULL) {
@@ -1851,7 +1766,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
         * For now we only do iface stats when the uid-owner is not requested
         */
        if (!(info->match & XT_QTAGUID_UID))
-               account_for_uid(skb, sk, sock_uid, par);
+               account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
 
        /*
         * The following two tests fail the match when:
@@ -1859,31 +1774,40 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
         * or id     in range AND    inverted condition requested
         * Thus (!a && b) || (a && !b) == a ^ b
         */
-       if (info->match & XT_QTAGUID_UID)
-               if ((filp->f_cred->fsuid >= info->uid_min &&
-                    filp->f_cred->fsuid <= info->uid_max) ^
+       if (info->match & XT_QTAGUID_UID) {
+               kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
+               kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
+
+               if ((uid_gte(filp->f_cred->fsuid, uid_min) &&
+                    uid_lte(filp->f_cred->fsuid, uid_max)) ^
                    !(info->invert & XT_QTAGUID_UID)) {
                        MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
                                 par->hooknum);
                        res = false;
                        goto put_sock_ret_res;
                }
-       if (info->match & XT_QTAGUID_GID)
-               if ((filp->f_cred->fsgid >= info->gid_min &&
-                               filp->f_cred->fsgid <= info->gid_max) ^
+       }
+       if (info->match & XT_QTAGUID_GID) {
+               kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
+               kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
+
+               if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
+                               gid_lte(filp->f_cred->fsgid, gid_max)) ^
                        !(info->invert & XT_QTAGUID_GID)) {
                        MT_DEBUG("qtaguid[%d]: leaving gid not matching\n",
                                par->hooknum);
                        res = false;
                        goto put_sock_ret_res;
                }
-
+       }
        MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum);
        res = true;
 
 put_sock_ret_res:
        if (got_sock)
-               xt_socket_put_sk(sk);
+               sock_gen_put(sk);
+       if (set_sk_callback_lock)
+               read_unlock_bh(&sk->sk_callback_lock);
 ret_res:
        MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res);
        return res;
@@ -1933,43 +1857,85 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...)
 static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
 #endif
 
+struct proc_ctrl_print_info {
+       struct sock *sk; /* socket found by reading to sk_pos */
+       loff_t sk_pos;
+};
+
+static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       struct proc_ctrl_print_info *pcpi = m->private;
+       struct sock_tag *sock_tag_entry = v;
+       struct rb_node *node;
+
+       (*pos)++;
+
+       if (!v || v  == SEQ_START_TOKEN)
+               return NULL;
+
+       node = rb_next(&sock_tag_entry->sock_node);
+       if (!node) {
+               pcpi->sk = NULL;
+               sock_tag_entry = SEQ_START_TOKEN;
+       } else {
+               sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+               pcpi->sk = sock_tag_entry->sk;
+       }
+       pcpi->sk_pos = *pos;
+       return sock_tag_entry;
+}
+
+static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos)
+{
+       struct proc_ctrl_print_info *pcpi = m->private;
+       struct sock_tag *sock_tag_entry;
+       struct rb_node *node;
+
+       spin_lock_bh(&sock_tag_list_lock);
+
+       if (unlikely(module_passive))
+               return NULL;
+
+       if (*pos == 0) {
+               pcpi->sk_pos = 0;
+               node = rb_first(&sock_tag_tree);
+               if (!node) {
+                       pcpi->sk = NULL;
+                       return SEQ_START_TOKEN;
+               }
+               sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+               pcpi->sk = sock_tag_entry->sk;
+       } else {
+               sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) :
+                                               NULL) ?: SEQ_START_TOKEN;
+               if (*pos != pcpi->sk_pos) {
+                       /* seq_read skipped a next call */
+                       *pos = pcpi->sk_pos;
+                       return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos);
+               }
+       }
+       return sock_tag_entry;
+}
+
+static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v)
+{
+       spin_unlock_bh(&sock_tag_list_lock);
+}
+
 /*
  * Procfs reader to get all active socket tags using style "1)" as described in
  * fs/proc/generic.c
  */
-static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
-                                 off_t items_to_skip, int char_count, int *eof,
-                                 void *data)
+static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
 {
-       char *outp = page;
-       int len;
+       struct sock_tag *sock_tag_entry = v;
        uid_t uid;
-       struct rb_node *node;
-       struct sock_tag *sock_tag_entry;
-       int item_index = 0;
-       int indent_level = 0;
        long f_count;
 
-       if (unlikely(module_passive)) {
-               *eof = 1;
-               return 0;
-       }
-
-       if (*eof)
-               return 0;
-
-       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);
+       CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
-       spin_lock_bh(&sock_tag_list_lock);
-       for (node = rb_first(&sock_tag_tree);
-            node;
-            node = rb_next(node)) {
-               if (item_index++ < items_to_skip)
-                       continue;
-               sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
+       if (sock_tag_entry != SEQ_START_TOKEN) {
                uid = get_uid_from_tag(sock_tag_entry->tag);
                CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) "
                         "pid=%u\n",
@@ -1980,66 +1946,42 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
                        );
                f_count = atomic_long_read(
                        &sock_tag_entry->socket->file->f_count);
-               len = snprintf(outp, char_count,
-                              "sock=%p tag=0x%llx (uid=%u) pid=%u "
-                              "f_count=%lu\n",
-                              sock_tag_entry->sk,
-                              sock_tag_entry->tag, uid,
-                              sock_tag_entry->pid, f_count);
-               if (len >= char_count) {
-                       spin_unlock_bh(&sock_tag_list_lock);
-                       *outp = '\0';
-                       return outp - page;
-               }
-               outp += len;
-               char_count -= len;
-               (*num_items_returned)++;
-       }
-       spin_unlock_bh(&sock_tag_list_lock);
-
-       if (item_index++ >= items_to_skip) {
-               len = snprintf(outp, char_count,
-                              "events: sockets_tagged=%llu "
-                              "sockets_untagged=%llu "
-                              "counter_set_changes=%llu "
-                              "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 "
-                              "match_no_sk=%llu "
-                              "match_no_sk_file=%llu\n",
-                              atomic64_read(&qtu_events.sockets_tagged),
-                              atomic64_read(&qtu_events.sockets_untagged),
-                              atomic64_read(&qtu_events.counter_set_changes),
-                              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(
-                                      &qtu_events.match_found_no_sk_in_ct),
-                              atomic64_read(&qtu_events.match_no_sk),
-                              atomic64_read(&qtu_events.match_no_sk_file));
-               if (len >= char_count) {
-                       *outp = '\0';
-                       return outp - page;
-               }
-               outp += len;
-               char_count -= len;
-               (*num_items_returned)++;
-       }
-
-       /* Count the following as part of the last item_index */
-       if (item_index > items_to_skip) {
-               prdebug_full_state(indent_level, "proc ctrl");
+               seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u "
+                          "f_count=%lu\n",
+                          sock_tag_entry->sk,
+                          sock_tag_entry->tag, uid,
+                          sock_tag_entry->pid, f_count);
+       } else {
+               seq_printf(m, "events: sockets_tagged=%llu "
+                          "sockets_untagged=%llu "
+                          "counter_set_changes=%llu "
+                          "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 "
+                          "match_no_sk=%llu "
+                          "match_no_sk_file=%llu\n",
+                          (u64)atomic64_read(&qtu_events.sockets_tagged),
+                          (u64)atomic64_read(&qtu_events.sockets_untagged),
+                          (u64)atomic64_read(&qtu_events.counter_set_changes),
+                          (u64)atomic64_read(&qtu_events.delete_cmds),
+                          (u64)atomic64_read(&qtu_events.iface_events),
+                          (u64)atomic64_read(&qtu_events.match_calls),
+                          (u64)atomic64_read(&qtu_events.match_calls_prepost),
+                          (u64)atomic64_read(&qtu_events.match_found_sk),
+                          (u64)atomic64_read(&qtu_events.match_found_sk_in_ct),
+                          (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct),
+                          (u64)atomic64_read(&qtu_events.match_no_sk),
+                          (u64)atomic64_read(&qtu_events.match_no_sk_file));
+
+               /* Count the following as part of the last item_index */
+               prdebug_full_state(0, "proc ctrl");
        }
 
-       *eof = 1;
-       return outp - page;
+       return 0;
 }
 
 /*
@@ -2049,7 +1991,8 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
 static int ctrl_cmd_delete(const char *input)
 {
        char cmd;
-       uid_t uid;
+       int uid_int;
+       kuid_t uid;
        uid_t entry_uid;
        tag_t acct_tag;
        tag_t tag;
@@ -2063,10 +2006,11 @@ static int ctrl_cmd_delete(const char *input)
        struct tag_ref *tr_entry;
        struct uid_tag_data *utd_entry;
 
-       argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid);
+       argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int);
+       uid = make_kuid(&init_user_ns, uid_int);
        CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c "
                 "user_tag=0x%llx uid=%u\n", input, argc, cmd,
-                acct_tag, uid);
+                acct_tag, uid_int);
        if (argc < 2) {
                res = -EINVAL;
                goto err;
@@ -2078,18 +2022,19 @@ static int ctrl_cmd_delete(const char *input)
        }
        if (argc < 3) {
                uid = current_fsuid();
+               uid_int = from_kuid(&init_user_ns, uid);
        } else if (!can_impersonate_uid(uid)) {
                pr_info("qtaguid: ctrl_delete(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err;
        }
 
-       tag = combine_atag_with_uid(acct_tag, uid);
+       tag = combine_atag_with_uid(acct_tag, uid_int);
        CT_DEBUG("qtaguid: ctrl_delete(%s): "
                 "looking for tag=0x%llx (uid=%u)\n",
-                input, tag, uid);
+                input, tag, uid_int);
 
        /* Delete socket tags */
        spin_lock_bh(&sock_tag_list_lock);
@@ -2098,7 +2043,7 @@ static int ctrl_cmd_delete(const char *input)
                st_entry = rb_entry(node, struct sock_tag, sock_node);
                entry_uid = get_uid_from_tag(st_entry->tag);
                node = rb_next(node);
-               if (entry_uid != uid)
+               if (entry_uid != uid_int)
                        continue;
 
                CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n",
@@ -2159,7 +2104,7 @@ static int ctrl_cmd_delete(const char *input)
                                 "ts tag=0x%llx (uid=%u)\n",
                                 input, ts_entry->tn.tag, entry_uid);
 
-                       if (entry_uid != uid)
+                       if (entry_uid != uid_int)
                                continue;
                        if (!acct_tag || ts_entry->tn.tag == tag) {
                                CT_DEBUG("qtaguid: ctrl_delete(%s): "
@@ -2188,7 +2133,7 @@ static int ctrl_cmd_delete(const char *input)
                         "utd uid=%u\n",
                         input, entry_uid);
 
-               if (entry_uid != uid)
+               if (entry_uid != uid_int)
                        continue;
                /*
                 * Go over the tag_refs, and those that don't have
@@ -2232,7 +2177,7 @@ static int ctrl_cmd_counter_set(const char *input)
        if (!can_manipulate_uids()) {
                pr_info("qtaguid: ctrl_counterset(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err;
        }
@@ -2269,7 +2214,8 @@ static int ctrl_cmd_tag(const char *input)
 {
        char cmd;
        int sock_fd = 0;
-       uid_t uid = 0;
+       kuid_t uid;
+       unsigned int uid_int = 0;
        tag_t acct_tag = make_atag_from_value(0);
        tag_t full_tag;
        struct socket *el_socket;
@@ -2280,10 +2226,11 @@ static int ctrl_cmd_tag(const char *input)
        struct proc_qtu_data *pqd_entry;
 
        /* Unassigned args will get defaulted later. */
-       argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid);
+       argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int);
+       uid = make_kuid(&init_user_ns, uid_int);
        CT_DEBUG("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);
+                acct_tag, uid_int);
        if (argc < 2) {
                res = -EINVAL;
                goto err;
@@ -2293,7 +2240,7 @@ static int ctrl_cmd_tag(const char *input)
                pr_info("qtaguid: ctrl_tag(%s): failed to lookup"
                        " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
                        input, sock_fd, res, current->pid, current->tgid,
-                       current_fsuid());
+                       from_kuid(&init_user_ns, current_fsuid()));
                goto err;
        }
        CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2309,21 +2256,24 @@ static int ctrl_cmd_tag(const char *input)
        CT_DEBUG("qtaguid: ctrl_tag(%s): "
                 "pid=%u tgid=%u uid=%u euid=%u fsuid=%u "
                 "ctrl.gid=%u in_group()=%d in_egroup()=%d\n",
-                input, current->pid, current->tgid, current_uid(),
-                current_euid(), current_fsuid(),
-                xt_qtaguid_ctrl_file->gid,
+                input, current->pid, current->tgid,
+                from_kuid(&init_user_ns, current_uid()),
+                from_kuid(&init_user_ns, current_euid()),
+                from_kuid(&init_user_ns, current_fsuid()),
+                from_kgid(&init_user_ns, xt_qtaguid_ctrl_file->gid),
                 in_group_p(xt_qtaguid_ctrl_file->gid),
                 in_egroup_p(xt_qtaguid_ctrl_file->gid));
        if (argc < 4) {
                uid = current_fsuid();
+               uid_int = from_kuid(&init_user_ns, uid);
        } else if (!can_impersonate_uid(uid)) {
                pr_info("qtaguid: ctrl_tag(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err_put;
        }
-       full_tag = combine_atag_with_uid(acct_tag, uid);
+       full_tag = combine_atag_with_uid(acct_tag, uid_int);
 
        spin_lock_bh(&sock_tag_list_lock);
        sock_tag_entry = get_sock_stat_nl(el_socket->sk);
@@ -2370,8 +2320,7 @@ static int ctrl_cmd_tag(const char *input)
                sock_tag_entry->sk = el_socket->sk;
                sock_tag_entry->socket = el_socket;
                sock_tag_entry->pid = current->tgid;
-               sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
-                                                           uid);
+               sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int);
                spin_lock_bh(&uid_tag_data_tree_lock);
                pqd_entry = proc_qtu_data_tree_search(
                        &proc_qtu_data_tree, current->tgid);
@@ -2386,7 +2335,7 @@ static int ctrl_cmd_tag(const char *input)
                                "User space forgot to open /dev/xt_qtaguid? "
                                "pid=%u tgid=%u uid=%u\n", __func__,
                                current->pid, current->tgid,
-                               current_fsuid());
+                               from_kuid(&init_user_ns, current_fsuid()));
                else
                        list_add(&sock_tag_entry->list,
                                 &pqd_entry->sock_tag_list);
@@ -2441,7 +2390,7 @@ static int ctrl_cmd_untag(const char *input)
                pr_info("qtaguid: ctrl_untag(%s): failed to lookup"
                        " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
                        input, sock_fd, res, current->pid, current->tgid,
-                       current_fsuid());
+                       from_kuid(&init_user_ns, current_fsuid()));
                goto err;
        }
        CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2475,7 +2424,7 @@ static int ctrl_cmd_untag(const char *input)
                pr_warn_once("qtaguid: %s(): "
                             "User space forgot to open /dev/xt_qtaguid? "
                             "pid=%u tgid=%u uid=%u\n", __func__,
-                            current->pid, current->tgid, current_fsuid());
+                            current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
        else
                list_del(&sock_tag_entry->list);
        spin_unlock_bh(&uid_tag_data_tree_lock);
@@ -2512,13 +2461,13 @@ err:
        return res;
 }
 
-static int qtaguid_ctrl_parse(const char *input, int count)
+static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
 {
        char cmd;
-       int res;
+       ssize_t res;
 
        CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n",
-                input, current->pid, current->tgid, current_fsuid());
+                input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        cmd = input[0];
        /* Collect params for commands */
@@ -2546,13 +2495,13 @@ static int qtaguid_ctrl_parse(const char *input, int count)
        if (!res)
                res = count;
 err:
-       CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res);
+       CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res);
        return res;
 }
 
 #define MAX_QTAGUID_CTRL_INPUT_LEN 255
-static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
-                       unsigned long count, void *data)
+static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
+                                  size_t count, loff_t *offp)
 {
        char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN];
 
@@ -2570,178 +2519,230 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
 }
 
 struct proc_print_info {
-       char *outp;
-       char **num_items_returned;
        struct iface_stat *iface_entry;
-       struct tag_stat *ts_entry;
        int item_index;
-       int items_to_skip;
-       int char_count;
+       tag_t tag; /* tag found by reading to tag_pos */
+       off_t tag_pos;
+       int tag_item_index;
 };
 
-static int pp_stats_line(struct proc_print_info *ppi, int cnt_set)
+static void pp_stats_header(struct seq_file *m)
 {
-       int len;
-       struct data_counters *cnts;
+       seq_puts(m,
+                "idx iface acct_tag_hex uid_tag_int cnt_set "
+                "rx_bytes rx_packets "
+                "tx_bytes tx_packets "
+                "rx_tcp_bytes rx_tcp_packets "
+                "rx_udp_bytes rx_udp_packets "
+                "rx_other_bytes rx_other_packets "
+                "tx_tcp_bytes tx_tcp_packets "
+                "tx_udp_bytes tx_udp_packets "
+                "tx_other_bytes tx_other_packets\n");
+}
 
-       if (!ppi->item_index) {
-               if (ppi->item_index++ < ppi->items_to_skip)
-                       return 0;
-               len = snprintf(ppi->outp, ppi->char_count,
-                              "idx iface acct_tag_hex uid_tag_int cnt_set "
-                              "rx_bytes rx_packets "
-                              "tx_bytes tx_packets "
-                              "rx_tcp_bytes rx_tcp_packets "
-                              "rx_udp_bytes rx_udp_packets "
-                              "rx_other_bytes rx_other_packets "
-                              "tx_tcp_bytes tx_tcp_packets "
-                              "tx_udp_bytes tx_udp_packets "
-                              "tx_other_bytes tx_other_packets\n");
-       } else {
-               tag_t tag = ppi->ts_entry->tn.tag;
-               uid_t stat_uid = get_uid_from_tag(tag);
-               /* Detailed tags are not available to everybody */
-               if (get_atag_from_tag(tag)
-                   && !can_read_other_uid_stats(stat_uid)) {
-                       CT_DEBUG("qtaguid: stats line: "
-                                "%s 0x%llx %u: insufficient priv "
-                                "from pid=%u tgid=%u uid=%u stats.gid=%u\n",
-                                ppi->iface_entry->ifname,
-                                get_atag_from_tag(tag), stat_uid,
-                                current->pid, current->tgid, current_fsuid(),
-                                xt_qtaguid_stats_file->gid);
-                       return 0;
-               }
-               if (ppi->item_index++ < ppi->items_to_skip)
-                       return 0;
-               cnts = &ppi->ts_entry->counters;
-               len = snprintf(
-                       ppi->outp, ppi->char_count,
-                       "%d %s 0x%llx %u %u "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu "
-                       "%llu %llu\n",
-                       ppi->item_index,
-                       ppi->iface_entry->ifname,
-                       get_atag_from_tag(tag),
-                       stat_uid,
-                       cnt_set,
-                       dc_sum_bytes(cnts, cnt_set, IFS_RX),
-                       dc_sum_packets(cnts, cnt_set, IFS_RX),
-                       dc_sum_bytes(cnts, cnt_set, IFS_TX),
-                       dc_sum_packets(cnts, cnt_set, IFS_TX),
-                       cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
-                       cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
-                       cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
-       }
-       return len;
-}
-
-static bool pp_sets(struct proc_print_info *ppi)
-{
-       int len;
+static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry,
+                        int cnt_set)
+{
+       struct data_counters *cnts;
+       tag_t tag = ts_entry->tn.tag;
+       uid_t stat_uid = get_uid_from_tag(tag);
+       struct proc_print_info *ppi = m->private;
+       /* Detailed tags are not available to everybody */
+       if (get_atag_from_tag(tag) && !can_read_other_uid_stats(
+                                               make_kuid(&init_user_ns,stat_uid))) {
+               CT_DEBUG("qtaguid: stats line: "
+                        "%s 0x%llx %u: insufficient priv "
+                        "from pid=%u tgid=%u uid=%u stats.gid=%u\n",
+                        ppi->iface_entry->ifname,
+                        get_atag_from_tag(tag), stat_uid,
+                        current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()),
+                        from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid));
+               return 0;
+       }
+       ppi->item_index++;
+       cnts = &ts_entry->counters;
+       seq_printf(m, "%d %s 0x%llx %u %u "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu "
+               "%llu %llu\n",
+               ppi->item_index,
+               ppi->iface_entry->ifname,
+               get_atag_from_tag(tag),
+               stat_uid,
+               cnt_set,
+               dc_sum_bytes(cnts, cnt_set, IFS_RX),
+               dc_sum_packets(cnts, cnt_set, IFS_RX),
+               dc_sum_bytes(cnts, cnt_set, IFS_TX),
+               dc_sum_packets(cnts, cnt_set, IFS_TX),
+               cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes,
+               cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets,
+               cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes,
+               cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets,
+               cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes,
+               cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets,
+               cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes,
+               cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets,
+               cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes,
+               cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
+               cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
+               cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
+       return seq_has_overflowed(m) ? -ENOSPC : 1;
+}
+
+static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry)
+{
+       int ret;
        int counter_set;
        for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS;
             counter_set++) {
-               len = pp_stats_line(ppi, counter_set);
-               if (len >= ppi->char_count) {
-                       *ppi->outp = '\0';
+               ret = pp_stats_line(m, ts_entry, counter_set);
+               if (ret < 0)
                        return false;
-               }
-               if (len) {
-                       ppi->outp += len;
-                       ppi->char_count -= len;
-                       (*ppi->num_items_returned)++;
-               }
        }
        return true;
 }
 
-/*
- * Procfs reader to get all tag stats using style "1)" as described in
- * fs/proc/generic.c
- * Groups all protocols tx/rx bytes.
- */
-static int qtaguid_stats_proc_read(char *page, char **num_items_returned,
-                               off_t items_to_skip, int char_count, int *eof,
-                               void *data)
-{
-       struct proc_print_info ppi;
-       int len;
-
-       ppi.outp = page;
-       ppi.item_index = 0;
-       ppi.char_count = char_count;
-       ppi.num_items_returned = num_items_returned;
-       ppi.items_to_skip = items_to_skip;
-
-       if (unlikely(module_passive)) {
-               len = pp_stats_line(&ppi, 0);
-               /* The header should always be shorter than the buffer. */
-               BUG_ON(len >= ppi.char_count);
-               (*num_items_returned)++;
-               *eof = 1;
-               return len;
-       }
-
-       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;
+static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr)
+{
+       struct iface_stat *iface_entry;
+
+       if (!ptr)
+               return false;
 
-       /* The idx is there to help debug when things go belly up. */
-       len = pp_stats_line(&ppi, 0);
-       /* Don't advance the outp unless the whole line was printed */
-       if (len >= ppi.char_count) {
-               *ppi.outp = '\0';
-               return ppi.outp - page;
+       list_for_each_entry(iface_entry, &iface_stat_list, list)
+               if (iface_entry == ptr)
+                       return true;
+       return false;
+}
+
+static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi)
+{
+       spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock);
+       list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) {
+               spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+               return;
+       }
+       ppi->iface_entry = NULL;
+}
+
+static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       struct proc_print_info *ppi = m->private;
+       struct tag_stat *ts_entry;
+       struct rb_node *node;
+
+       if (!v) {
+               pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__);
+               return NULL;
        }
-       if (len) {
-               ppi.outp += len;
-               ppi.char_count -= len;
-               (*num_items_returned)++;
+
+       (*pos)++;
+
+       if (!ppi->iface_entry || unlikely(module_passive))
+               return NULL;
+
+       if (v == SEQ_START_TOKEN)
+               node = rb_first(&ppi->iface_entry->tag_stat_tree);
+       else
+               node = rb_next(&((struct tag_stat *)v)->tn.node);
+
+       while (!node) {
+               qtaguid_stats_proc_next_iface_entry(ppi);
+               if (!ppi->iface_entry)
+                       return NULL;
+               node = rb_first(&ppi->iface_entry->tag_stat_tree);
        }
 
+       ts_entry = rb_entry(node, struct tag_stat, tn.node);
+       ppi->tag = ts_entry->tn.tag;
+       ppi->tag_pos = *pos;
+       ppi->tag_item_index = ppi->item_index;
+       return ts_entry;
+}
+
+static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos)
+{
+       struct proc_print_info *ppi = m->private;
+       struct tag_stat *ts_entry = NULL;
+
        spin_lock_bh(&iface_stat_list_lock);
-       list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) {
-               struct rb_node *node;
-               spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock);
-               for (node = rb_first(&ppi.iface_entry->tag_stat_tree);
-                    node;
-                    node = rb_next(node)) {
-                       ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node);
-                       if (!pp_sets(&ppi)) {
-                               spin_unlock_bh(
-                                       &ppi.iface_entry->tag_stat_list_lock);
-                               spin_unlock_bh(&iface_stat_list_lock);
-                               return ppi.outp - page;
-                       }
+
+       if (*pos == 0) {
+               ppi->item_index = 1;
+               ppi->tag_pos = 0;
+               if (list_empty(&iface_stat_list)) {
+                       ppi->iface_entry = NULL;
+               } else {
+                       ppi->iface_entry = list_first_entry(&iface_stat_list,
+                                                           struct iface_stat,
+                                                           list);
+                       spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+               }
+               return SEQ_START_TOKEN;
+       }
+       if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) {
+               if (ppi->iface_entry) {
+                       pr_err("qtaguid: %s(): iface_entry %p not found\n",
+                              __func__, ppi->iface_entry);
+                       ppi->iface_entry = NULL;
                }
-               spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock);
+               return NULL;
+       }
+
+       spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock);
+
+       if (!ppi->tag_pos) {
+               /* seq_read skipped first next call */
+               ts_entry = SEQ_START_TOKEN;
+       } else {
+               ts_entry = tag_stat_tree_search(
+                               &ppi->iface_entry->tag_stat_tree, ppi->tag);
+               if (!ts_entry) {
+                       pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n",
+                               __func__, ppi->tag);
+                       return NULL;
+               }
+       }
+
+       if (*pos == ppi->tag_pos) { /* normal resume */
+               ppi->item_index = ppi->tag_item_index;
+       } else {
+               /* seq_read skipped a next call */
+               *pos = ppi->tag_pos;
+               ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos);
        }
+
+       return ts_entry;
+}
+
+static void qtaguid_stats_proc_stop(struct seq_file *m, void *v)
+{
+       struct proc_print_info *ppi = m->private;
+       if (ppi->iface_entry)
+               spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock);
        spin_unlock_bh(&iface_stat_list_lock);
+}
+
+/*
+ * Procfs reader to get all tag stats using style "1)" as described in
+ * fs/proc/generic.c
+ * Groups all protocols tx/rx bytes.
+ */
+static int qtaguid_stats_proc_show(struct seq_file *m, void *v)
+{
+       struct tag_stat *ts_entry = v;
+
+       if (v == SEQ_START_TOKEN)
+               pp_stats_header(m);
+       else
+               pp_sets(m, ts_entry);
 
-       *eof = 1;
-       return ppi.outp - page;
+       return 0;
 }
 
 /*------------------------------------------*/
@@ -2757,12 +2758,12 @@ static int qtudev_open(struct inode *inode, struct file *file)
                return 0;
 
        DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n",
-                current->pid, current->tgid, current_fsuid());
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        spin_lock_bh(&uid_tag_data_tree_lock);
 
        /* Look for existing uid data, or alloc one. */
-       utd_entry = get_uid_data(current_fsuid(), &utd_entry_found);
+       utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found);
        if (IS_ERR_OR_NULL(utd_entry)) {
                res = PTR_ERR(utd_entry);
                goto err_unlock;
@@ -2774,7 +2775,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
        if (pqd_entry) {
                pr_err("qtaguid: qtudev_open(): %u/%u %u "
                       "%s already opened\n",
-                      current->pid, current->tgid, current_fsuid(),
+                      current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()),
                       QTU_DEV_NAME);
                res = -EBUSY;
                goto err_unlock_free_utd;
@@ -2784,7 +2785,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
        if (!new_pqd_entry) {
                pr_err("qtaguid: qtudev_open(): %u/%u %u: "
                       "proc data alloc failed\n",
-                      current->pid, current->tgid, current_fsuid());
+                      current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -ENOMEM;
                goto err_unlock_free_utd;
        }
@@ -2798,7 +2799,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
 
        spin_unlock_bh(&uid_tag_data_tree_lock);
        DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n",
-                current_fsuid(), new_pqd_entry);
+                from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry);
        file->private_data = new_pqd_entry;
        return 0;
 
@@ -2906,6 +2907,47 @@ static struct miscdevice qtu_device = {
        /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */
 };
 
+static const struct seq_operations proc_qtaguid_ctrl_seqops = {
+       .start = qtaguid_ctrl_proc_start,
+       .next = qtaguid_ctrl_proc_next,
+       .stop = qtaguid_ctrl_proc_stop,
+       .show = qtaguid_ctrl_proc_show,
+};
+
+static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file)
+{
+       return seq_open_private(file, &proc_qtaguid_ctrl_seqops,
+                               sizeof(struct proc_ctrl_print_info));
+}
+
+static const struct file_operations proc_qtaguid_ctrl_fops = {
+       .open           = proc_qtaguid_ctrl_open,
+       .read           = seq_read,
+       .write          = qtaguid_ctrl_proc_write,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
+static const struct seq_operations proc_qtaguid_stats_seqops = {
+       .start = qtaguid_stats_proc_start,
+       .next = qtaguid_stats_proc_next,
+       .stop = qtaguid_stats_proc_stop,
+       .show = qtaguid_stats_proc_show,
+};
+
+static int proc_qtaguid_stats_open(struct inode *inode, struct file *file)
+{
+       return seq_open_private(file, &proc_qtaguid_stats_seqops,
+                               sizeof(struct proc_print_info));
+}
+
+static const struct file_operations proc_qtaguid_stats_fops = {
+       .open           = proc_qtaguid_stats_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
 /*------------------------------------------*/
 static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
 {
@@ -2917,26 +2959,27 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir)
                goto no_dir;
        }
 
-       xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms,
-                                               *res_procdir);
+       xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms,
+                                               *res_procdir,
+                                               &proc_qtaguid_ctrl_fops,
+                                               NULL);
        if (!xt_qtaguid_ctrl_file) {
                pr_err("qtaguid: failed to create xt_qtaguid/ctrl "
                        " file\n");
                ret = -ENOMEM;
                goto no_ctrl_entry;
        }
-       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", proc_stats_perms,
-                                               *res_procdir);
+       xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms,
+                                                *res_procdir,
+                                                &proc_qtaguid_stats_fops,
+                                                NULL);
        if (!xt_qtaguid_stats_file) {
                pr_err("qtaguid: failed to create xt_qtaguid/stats "
                        "file\n");
                ret = -ENOMEM;
                goto no_stats_entry;
        }
-       xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read;
        /*
         * TODO: add support counter hacking
         * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write;