[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