#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>
#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
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);
{
/* 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,
"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);
}
}
-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)
}
/* 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;
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);
}
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);
"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 */
* {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} */
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;
.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;
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);
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;
}
}
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;
}
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
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) {
* 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:
* 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;
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",
);
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;
}
/*
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;
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;
}
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);
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",
"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): "
"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
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;
}
{
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;
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;
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",
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);
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);
"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);
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",
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);
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 */
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];
}
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;
}
/*------------------------------------------*/
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;
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;
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;
}
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;
/* 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)
{
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;