[Devel] [PATCH RHEL7 COMMIT] vzprivnet: Sparse privnets support

Konstantin Khorenko khorenko at virtuozzo.com
Thu Mar 24 08:53:29 PDT 2016


The commit is pushed to "branch-rh7-3.10.0-327.10.1.vz7.12.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-327.10.1.vz7.12.3
------>
commit 7d3aac381027894ddf87d4e71ce9d66e90d29bb8
Author: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Date:   Thu Mar 24 19:53:29 2016 +0400

    vzprivnet: Sparse privnets support
    
    changes:
    1) replace NIPQUAD_FMT with %pI4 in sparse_seq_show
    2) include inet.h for in4_pton
    
    Port diff-vz-privnet-support-sparse
      vzprivnet: Sparse privnets support
    
      The API is - /proc/vz/privnet/sparse with the following rules:
    
       +ID                 to add a network
       +ID:a.b.c.d         to add an IP to network
       +ID:a.b.c.d/m       to add a subnet to network
       -ID                 to remove the whole network
       -a.b.c.d            to remove an IP or bounding subnet (from its
      network)
    
      Matching rules are stored in the rbtree in the legacy rules manner.
    
      Signed-off-by: Pavel Emelyanov <xemul at parallels.com>
    
    Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
 net/ipv4/netfilter/ip_vzprivnet.c | 351 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 350 insertions(+), 1 deletion(-)

diff --git a/net/ipv4/netfilter/ip_vzprivnet.c b/net/ipv4/netfilter/ip_vzprivnet.c
index 8a8520a..2a8beb1 100644
--- a/net/ipv4/netfilter/ip_vzprivnet.c
+++ b/net/ipv4/netfilter/ip_vzprivnet.c
@@ -27,6 +27,7 @@
 #include <linux/proc_fs.h>
 #include <linux/log2.h>
 #include <linux/ctype.h>
+#include <linux/inet.h>
 #include <asm/page.h>
 
 #define VZPRIV_PROCNAME "ip_vzprivnet"
@@ -53,6 +54,14 @@ struct vzprivnet {
 	int weak;
 };
 
+struct vzprivnet_sparse {
+	struct vzprivnet pn;
+
+	unsigned int netid;
+	struct list_head list;
+	struct list_head entries;
+};
+
 struct vzprivnet_range {
 	struct vzprivnet *pn;
 
@@ -62,7 +71,14 @@ struct vzprivnet_range {
 	struct rb_node node;
 };
 
+struct vzprivnet_entry {
+	struct vzprivnet_range range;
+	struct list_head list;
+};
+
 static DEFINE_RWLOCK(vzprivlock);
+static LIST_HEAD(vzpriv_sparse);
+static struct rb_root entries_root = RB_ROOT;
 
 /*
  * Tree helpers
@@ -172,7 +188,10 @@ static struct vzprivnet *vzpriv_search(u32 ip)
 {
 	struct vzprivnet_range *pnr;
 
-	pnr = legacy_search(ip);
+	pnr = tree_search(&entries_root, ip);
+	if (pnr == NULL)
+		pnr = legacy_search(ip);
+
 	if (pnr != NULL)
 		return pnr->pn;
 	else
@@ -307,9 +326,11 @@ static int vzprivnet_del(u32 net)
 	return 0;
 }
 
+static void sparse_free_one(struct vzprivnet_sparse *pns);
 static void vzprivnet_cleanup(void)
 {
 	struct vzprivnet_range *p;
+	struct vzprivnet_sparse *pns;
 
 	write_lock_bh(&vzprivlock);
 	while (1) {
@@ -320,6 +341,12 @@ static void vzprivnet_cleanup(void)
 		kfree(p->pn);
 		kfree(p);
 	}
+
+	while (!list_empty(&vzpriv_sparse)) {
+		pns = list_first_entry(&vzpriv_sparse,
+				struct vzprivnet_sparse, list);
+		sparse_free_one(pns);
+	}
 	write_unlock_bh(&vzprivlock);
 }
 
@@ -487,6 +514,320 @@ static struct file_operations proc_vzprivnet_ops = {
 	.write   = vzpriv_write,
 };
 
+static int sparse_add(unsigned int netid, u32 ip, u32 mask)
+{
+	int err;
+	struct vzprivnet_sparse *pns, *epns = NULL;
+	struct vzprivnet_entry *pne = NULL, *tmp;
+
+	err = -ENOMEM;
+
+	pns = kmalloc(sizeof(struct vzprivnet_sparse), GFP_KERNEL);
+	if (pns == NULL)
+		goto out;
+
+	pne = kmalloc(sizeof(struct vzprivnet_entry), GFP_KERNEL);
+	if (pne == NULL)
+		goto out;
+
+	write_lock_bh(&vzprivlock);
+	list_for_each_entry(epns, &vzpriv_sparse, list)
+		if (epns->netid == netid) {
+			pns = epns;
+			goto found_net;
+		}
+
+	pns->netid = netid;
+	pns->pn.nmask = 0;
+	pns->pn.weak = 0;
+	INIT_LIST_HEAD(&pns->entries);
+
+found_net:
+	if (ip != 0) {
+		ip &= mask;
+		list_for_each_entry(tmp, &pns->entries, list) {
+			if ((ip & tmp->range.rmask) == tmp->range.netip)
+				goto out_unlock;
+			if ((tmp->range.netip & mask) == ip)
+				goto out_unlock;
+		}
+
+		pne->range.netip = ip & mask;
+		pne->range.rmask = mask;
+		pne->range.pn = &pns->pn;
+		list_add_tail(&pne->list, &pns->entries);
+		tree_insert(&entries_root, &pne->range);
+		pne = NULL;
+	} else if (pns == epns) {
+		err = -EEXIST;
+		goto out_unlock;
+	}
+
+	if (pns != epns) {
+		list_add_tail(&pns->list, &vzpriv_sparse);
+		pns = NULL;
+	}
+
+	err = 0;
+
+out_unlock:
+	write_unlock_bh(&vzprivlock);
+out:
+	if (pns != epns)
+		kfree(pns);
+	kfree(pne);
+
+	return err;
+}
+
+static void sparse_free_entry(struct vzprivnet_entry *pne)
+{
+	list_del(&pne->list);
+	rb_erase(&pne->range.node, &entries_root);
+	kfree(pne);
+}
+
+static void sparse_free_one(struct vzprivnet_sparse *pns)
+{
+	struct vzprivnet_entry *pne;
+
+	list_del(&pns->list);
+
+	while (!list_empty(&pns->entries)) {
+		pne = list_first_entry(&pns->entries,
+				struct vzprivnet_entry, list);
+		sparse_free_entry(pne);
+	}
+
+	kfree(pns);
+}
+
+static int sparse_del_net(unsigned int netid)
+{
+	struct vzprivnet_sparse *pns;
+
+	list_for_each_entry(pns, &vzpriv_sparse, list)
+		if (pns->netid == netid) {
+			sparse_free_one(pns);
+			return 0;
+		}
+
+	return -ENOENT;
+}
+
+static int sparse_del_ip(u32 ip)
+{
+	struct vzprivnet_range *rng;
+	struct vzprivnet_entry *pne;
+
+	rng = tree_search(&entries_root, ip);
+	if (rng == NULL)
+		return -ENOENT;
+
+	pne = container_of(rng, struct vzprivnet_entry, range);
+	sparse_free_entry(pne);
+
+	return 0;
+}
+
+static int sparse_del(unsigned int netid, u32 ip)
+{
+	int err;
+
+	write_lock_bh(&vzprivlock);
+	if (ip != 0)
+		err = sparse_del_ip(ip);
+	else
+		err = sparse_del_net(netid);
+	write_unlock_bh(&vzprivlock);
+
+	return err;
+}
+
+/*
+ * +ID			to add a network
+ * +ID:a.b.c.d		to add an IP to network
+ * +ID:a.b.c.d/m	to add a subnet to network
+ * -ID			to remove the whole network
+ * -a.b.c.d		to remove an IP or bounding subnet (from its network)
+ *
+ *  No weak networks here!
+ */
+
+#define is_eol(ch)	((ch) == '\0' || (ch) == '\n')
+
+static int parse_sparse_add(const char *str, unsigned int *netid, u32 *ip, u32 *mask)
+{
+	unsigned int m;
+	char *end;
+
+	*netid = simple_strtol(str, &end, 10);
+	if (is_eol(*end)) {
+		*ip = 0;
+		return 0;
+	}
+
+	if (*end != ':')
+		return -EINVAL;
+
+	str = end + 1;
+	if (!in4_pton(str, -1, (u8 *)ip, -1, (const char **)&end))
+		return -EINVAL;
+
+	if (is_eol(*end)) {
+		*mask = -1; /* match only one IP */
+		return 0;
+	}
+
+	if (*end != '/')
+		return -EINVAL;
+
+	str = end + 1;
+	m = simple_strtol(str, &end, 10);
+	if (!is_eol(*end))
+		return -EINVAL;
+
+	*mask = to_netmask(m);
+	return 0;
+}
+
+static int parse_sparse_remove(const char *str, unsigned int *netid, u32 *ip)
+{
+	char *end;
+
+	if (strchr(str, '.')) {
+		if (!in4_pton(str, -1, (u8 *)ip, -1, (const char **)&end))
+			return -EINVAL;
+	} else
+		*netid = simple_strtol(str, &end, 10);
+
+	return (is_eol(*end) ? 0 : -EINVAL);
+}
+
+static int parse_sparse(const char *param, int *add,
+		unsigned int *netid, u32 *ip, u32 *mask)
+{
+	if (param[0] == '+') {
+		*add = 1;
+		return parse_sparse_add(param + 1, netid, ip, mask);
+	}
+
+	if (param[0] == '-') {
+		*add = 0;
+		return parse_sparse_remove(param + 1, netid, ip);
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t sparse_write(struct file * file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	char *s, *page;
+	int err;
+	int offset;
+
+	page = (unsigned char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	if (count > (PAGE_SIZE - 1))
+		count = (PAGE_SIZE - 1);
+
+	err = copy_from_user(page, buf, count);
+	if (err)
+		goto err;
+
+	s = page;
+	s[count] = 0;
+
+	err = -EINVAL;
+	while (*s) {
+		int add;
+		unsigned int netid = 0;
+		u32 ip = 0, mask = 0;
+
+		err = parse_sparse(s, &add, &netid, &ip, &mask);
+		if (err)
+			goto out;
+
+		if (add)
+			err = sparse_add(netid, ip, mask);
+		else
+			err = sparse_del(netid, ip);
+
+		if (err)
+			goto out;
+
+		s = nextline(s);
+	}
+out:
+	offset = s - page;
+	if (offset > 0)
+		err = offset;
+err:
+	free_page((unsigned long)page);
+	return err;
+}
+
+static void *sparse_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	read_lock_bh(&vzprivlock);
+	return seq_list_start(&vzpriv_sparse, *pos);
+}
+
+static void *sparse_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	return seq_list_next(v, &vzpriv_sparse, pos);
+}
+
+static void sparse_seq_stop(struct seq_file *s, void *v)
+{
+	read_unlock_bh(&vzprivlock);
+}
+
+static int sparse_seq_show(struct seq_file *s, void *v)
+{
+	struct list_head *lh = v;
+	struct vzprivnet_sparse *pns;
+	struct vzprivnet_entry *pne;
+
+	pns = list_entry(lh, struct vzprivnet_sparse, list);
+	seq_printf(s, "%u: ", pns->netid);
+
+	list_for_each_entry(pne, &pns->entries, list) {
+		seq_printf(s, "%pI4", &pne->range.netip);
+		if (~pne->range.rmask != 0) /* subnet */
+			seq_printf(s, "/%u", to_prefix(pne->range.rmask));
+		seq_putc(s, ' ');
+	}
+
+	seq_putc(s, '\n');
+
+	return 0;
+}
+
+static struct seq_operations sparse_seq_ops = {
+	.start = sparse_seq_start,
+	.next  = sparse_seq_next,
+	.stop  = sparse_seq_stop,
+	.show  = sparse_seq_show,
+};
+
+static int sparse_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &sparse_seq_ops);
+}
+
+static struct file_operations proc_sparse_ops = {
+	.owner   = THIS_MODULE,
+	.open    = sparse_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+	.write   = sparse_write,
+};
+
 static struct proc_dir_entry *vzpriv_proc_dir;
 
 static int __init iptable_vzprivnet_init(void)
@@ -503,6 +844,11 @@ static int __init iptable_vzprivnet_init(void)
 	if (proc == NULL)
 		goto err_legacy;
 
+	proc = proc_create("sparse", 0644,
+			vzpriv_proc_dir, &proc_sparse_ops);
+	if (proc == NULL)
+		goto err_net;
+
 	proc = proc_symlink(VZPRIV_PROCNAME, init_net.proc_net, "/proc/vz/privnet/legacy");
 	if (proc == NULL)
 		goto err_link;
@@ -516,6 +862,8 @@ static int __init iptable_vzprivnet_init(void)
 err_reg:
 	remove_proc_entry(VZPRIV_PROCNAME, init_net.proc_net);
 err_link:
+	remove_proc_entry("sparse", vzpriv_proc_dir);
+err_net:
 	remove_proc_entry("legacy", vzpriv_proc_dir);
 err_legacy:
 	remove_proc_entry("privnet", proc_vz_dir);
@@ -527,6 +875,7 @@ static void __exit iptable_vzprivnet_exit(void)
 {
 	nf_unregister_hook(&vzprivnet_ops);
 	remove_proc_entry(VZPRIV_PROCNAME, init_net.proc_net);
+	remove_proc_entry("sparse", vzpriv_proc_dir);
 	remove_proc_entry("legacy", vzpriv_proc_dir);
 	remove_proc_entry("privnet", proc_vz_dir);
 	vzprivnet_cleanup();


More information about the Devel mailing list