[Devel] [PATCH RHEL7 COMMIT] vzprivnet: Initial implementation of v6 privnets
Konstantin Khorenko
khorenko at virtuozzo.com
Thu Mar 24 08:53:34 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 b08e4c3b85efe3d81cf92de9fb1e8b97831edfcc
Author: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Date: Thu Mar 24 19:53:34 2016 +0400
vzprivnet: Initial implementation of v6 privnets
changes:
1) use skb instead of pskb in vzprivnet6_hook
was part of diff-vz-privnet6-rh6-compile
privnet: Use rh6 kapi
New nf callback prototype and constants
New ipv6 skb dst and header management routines
Built-in pton/ntop functionality
2) remove second loop cursor in hlist_for_each_entry
3) use ipv6_prefix_equal as there is no __ipv6_prefix_equal
4) include inet.h for in6_pton
5) make new vzprivnet6_hook correspond to nf_hookfn prototype
Port diff-vz-privnet-v6-module-plain
vzprivnet: Initial implementation of v6 privnets
This only declares the API and provides the playground for the
userspace.
The entries are stored and looked up at forward chain with the plain
list (hlist). Better searching engine will be done a bit later.
https://jira.sw.ru/browse/PCLIN-28727
Taken from 2.6.18-rh5
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
net/ipv6/netfilter/Makefile | 2 +
net/ipv6/netfilter/ip6_vzprivnet.c | 579 +++++++++++++++++++++++++++++++++++++
2 files changed, 581 insertions(+)
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index 0f7e5b3..8e72cc7 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -16,6 +16,8 @@ nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o
# l3 independent conntrack
obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o
+obj-$(CONFIG_VE_IP_NF_VZPRIVNET) += ip6_vzprivnet.o
+
nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o
obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o
diff --git a/net/ipv6/netfilter/ip6_vzprivnet.c b/net/ipv6/netfilter/ip6_vzprivnet.c
new file mode 100644
index 0000000..ffaf3ad
--- /dev/null
+++ b/net/ipv6/netfilter/ip6_vzprivnet.c
@@ -0,0 +1,579 @@
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/vzprivnet.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/inet.h>
+#include <net/ipv6.h>
+
+static DEFINE_RWLOCK(vzpriv6lock);
+
+struct vzprivnet {
+ unsigned int netid;
+ int weak;
+ struct list_head list;
+ struct list_head entries;
+};
+
+static LIST_HEAD(vzprivnets);
+
+struct vzprivnet_entry {
+ __u32 ip[4];
+ unsigned preflen;
+ struct vzprivnet *pn;
+ struct list_head list;
+ struct hlist_node hash;
+};
+
+static HLIST_HEAD(vzpriv_entries);
+
+static inline int ip6_match(u32 *net, unsigned plen, u32 *ip)
+{
+ return ipv6_prefix_equal((const struct in6_addr *)net, (const struct in6_addr *)ip, plen);
+}
+
+static inline int ip6_intersect(u32 *ip1, unsigned len1, u32 *ip2, unsigned len2)
+{
+ return ip6_match(ip1, len1, ip2) || ip6_match(ip2, len2, ip1);
+}
+
+static struct vzprivnet_entry *vzprivnet6_lookup(u32 *ip)
+{
+ struct vzprivnet_entry *e;
+
+ hlist_for_each_entry(e, &vzpriv_entries, hash) {
+ if (ip6_match(e->ip, e->preflen, ip))
+ return e;
+ }
+
+ return NULL;
+}
+
+struct vzprivnet internet = {
+ .weak = 1,
+};
+
+static inline struct vzprivnet *vzprivnet6_lookup_net(u32 *ip)
+{
+ struct vzprivnet_entry *pne;
+
+ pne = vzprivnet6_lookup(ip);
+ if (pne != NULL)
+ return pne->pn;
+ else
+ return &internet;
+}
+
+static void vzprivnet6_hash_entry(struct vzprivnet_entry *e)
+{
+ hlist_add_head(&e->hash, &vzpriv_entries);
+}
+
+static void vzprivnet6_unhash_entry(struct vzprivnet_entry *e)
+{
+ hlist_del(&e->hash);
+}
+
+static inline int noip(u32 *ip)
+{
+ return (ip[0] | ip[1] | ip[2] | ip[3]) == 0;
+}
+
+static int sparse6_add(unsigned netid, u32 *ip, unsigned preflen, int weak)
+{
+ int err;
+ struct vzprivnet *pn, *epn = NULL;
+ struct vzprivnet_entry *pne = NULL, *tmp;
+
+ err = -ENOMEM;
+ pn = kzalloc(sizeof(*pn), GFP_KERNEL);
+ if (pn == NULL)
+ goto out;
+
+ pne = kzalloc(sizeof(*pne), GFP_KERNEL);
+ if (pne == NULL)
+ goto out;
+
+ write_lock_bh(&vzpriv6lock);
+ list_for_each_entry(epn, &vzprivnets, list)
+ if (epn->netid == netid) {
+ kfree(pn);
+ pn = epn;
+ goto found_net;
+ }
+
+ pn->netid = netid;
+ pn->weak = weak;
+ INIT_LIST_HEAD(&pn->entries);
+
+found_net:
+ if (!noip(ip)) {
+ err = -EEXIST;
+ list_for_each_entry(tmp, &pn->entries, list)
+ if (ip6_intersect(ip, preflen, tmp->ip, tmp->preflen))
+ goto out_unlock;
+
+ memcpy(pne->ip, ip, sizeof(pne->ip));
+ pne->preflen = preflen;
+ pne->pn = pn;
+ list_add_tail(&pne->list, &pn->entries);
+ vzprivnet6_hash_entry(pne);
+ pne = NULL;
+ } else if (weak) {
+ pn->weak = 1;
+ } else if (pn == epn) {
+ err = -EEXIST;
+ goto out_unlock;
+ }
+
+ if (pn != epn) {
+ list_add_tail(&pn->list, &vzprivnets);
+ pn = NULL;
+ }
+
+ err = 0;
+
+out_unlock:
+ write_unlock_bh(&vzpriv6lock);
+out:
+ if (pn != epn)
+ kfree(pn);
+ kfree(pne);
+
+ return err;
+}
+
+static void sparse6_free_entry(struct vzprivnet_entry *pne)
+{
+ list_del(&pne->list);
+ vzprivnet6_unhash_entry(pne);
+ kfree(pne);
+}
+
+static void sparse6_del_one(struct vzprivnet *pn)
+{
+ struct vzprivnet_entry *pne;
+
+ list_del(&pn->list);
+
+ while (!list_empty(&pn->entries)) {
+ pne = list_first_entry(&pn->entries,
+ struct vzprivnet_entry, list);
+ sparse6_free_entry(pne);
+ }
+
+ kfree(pn);
+}
+
+static void vzprivnet6_cleanup(void)
+{
+ struct vzprivnet *pn;
+
+ write_lock_bh(&vzpriv6lock);
+ while (!list_empty(&vzprivnets)) {
+ pn = list_first_entry(&vzprivnets,
+ struct vzprivnet, list);
+ sparse6_del_one(pn);
+ }
+ write_unlock_bh(&vzpriv6lock);
+}
+
+static int sparse6_del_net(unsigned netid, int weak)
+{
+ struct vzprivnet *pn;
+
+ list_for_each_entry(pn, &vzprivnets, list) {
+ if (pn->netid != netid)
+ continue;
+
+ if (weak)
+ pn->weak = 0;
+ else
+ sparse6_del_one(pn);
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int sparse6_del_ip(u32 *ip)
+{
+ struct vzprivnet_entry *pne;
+
+ pne = vzprivnet6_lookup(ip);
+ if (pne == NULL)
+ return -ENOENT;
+
+ sparse6_free_entry(pne);
+ return 0;
+}
+
+static int sparse6_del(unsigned netid, u32 *ip, int weak)
+{
+ int err;
+
+ write_lock_bh(&vzpriv6lock);
+ if (!noip(ip))
+ err = sparse6_del_ip(ip);
+ else
+ err = sparse6_del_net(netid, weak);
+ write_unlock_bh(&vzpriv6lock);
+
+ return err;
+}
+
+static unsigned int vzprivnet6_hook(const struct nf_hook_ops *ops,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_hook_state *state)
+{
+ int verdict = NF_DROP;
+ struct vzprivnet *dst, *src;
+
+ read_lock(&vzpriv6lock);
+
+ src = vzprivnet6_lookup_net((*pskb)->nh.ipv6h->saddr.in6_u.u6_addr32);
+ dst = vzprivnet6_lookup_net((*pskb)->nh.ipv6h->daddr.in6_u.u6_addr32);
+
+ if (src == dst)
+ verdict = NF_ACCEPT;
+ else if (src->weak && dst->weak)
+ verdict = NF_ACCEPT;
+
+ read_unlock(&vzpriv6lock);
+
+ return verdict;
+}
+
+static struct nf_hook_ops vzprivnet6_ops = {
+ .hook = vzprivnet6_hook,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_IP6_FORWARD,
+ .priority = NF_IP6_PRI_FIRST
+};
+
+static char *nextline(char *s)
+{
+ while(*s && *s != '\n') s++;
+ while(*s && *s == '\n') s++;
+ return s;
+}
+
+static int parse_sparse6_add(const char *str, unsigned int *netid, u32 *ip, unsigned *preflen, int *weak)
+{
+ char *end;
+
+ *netid = simple_strtol(str, &end, 10);
+ if (is_eol(*end))
+ return 0;
+
+ if (*end != ':')
+ return -EINVAL;
+
+ str = end + 1;
+ if (*str == '*') {
+ if (!is_eol(*(str + 1)))
+ return -EINVAL;
+
+ *weak = 1;
+ return 0;
+ }
+
+ if (!in6_pton(str, ip, &end))
+ return -EINVAL;
+
+ if (*end != '/')
+ return -EINVAL;
+
+ str = end + 1;
+ *preflen = simple_strtol(str, &end, 10);
+ if (!is_eol(*end))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int parse_sparse6_remove(const char *str, unsigned int *netid, u32 *ip, int *weak)
+{
+ char *end;
+
+ if (strchr(str, ':') && !strchr(str, '*')) {
+ if (!in6_pton(str, ip, &end)) {
+ printk("Bad ip in %s\n", str);
+ return -EINVAL;
+ }
+
+ if (!is_eol(*end))
+ printk("No EOL in %s\n", str);
+ } else {
+ *netid = simple_strtol(str, &end, 10);
+ if (end[0] == ':' && end[1] == '*') {
+ end += 2;
+ *weak = 1;
+ }
+ }
+
+ return (is_eol(*end) ? 0 : -EINVAL);
+}
+
+static int parse_sparse6(const char *param, int *add,
+ unsigned int *netid, u32 *ip, unsigned *preflen, int *weak)
+{
+ if (param[0] == '+') {
+ *add = 1;
+ return parse_sparse6_add(param + 1, netid, ip, preflen, weak);
+ }
+
+ if (param[0] == '-') {
+ *add = 0;
+ return parse_sparse6_remove(param + 1, netid, ip, weak);
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * +ID to add a network
+ * +ID:<addr>/m to add a subnet to network
+ * +ID:* to make a network weak
+ * -ID to remove the whole network
+ * -<addr> to remove an IP or bounding subnet (from its network)
+ * -ID:* to make a network "strong" ;)
+ *
+ * No weak networks here!
+ */
+
+static ssize_t sparse6_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, weak = 0;
+ unsigned int netid = 0, preflen = 0;
+ u32 ip[4] = { 0, 0, 0, 0 };
+
+ err = parse_sparse6(s, &add, &netid, ip, &preflen, &weak);
+ if (err)
+ goto out;
+
+ if (add)
+ err = sparse6_add(netid, ip, preflen, weak);
+ else
+ err = sparse6_del(netid, ip, weak);
+
+ 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 *sparse6_seq_start(struct seq_file *seq, loff_t *ppos)
+{
+ struct list_head *lh;
+ loff_t pos = *ppos;
+
+ read_lock(&vzpriv6lock);
+ list_for_each(lh, &vzprivnets)
+ if (pos-- == 0)
+ return lh;
+
+ return NULL;
+}
+
+static void *sparse6_seq_next(struct seq_file *seq, void *v, loff_t *ppos)
+{
+ struct list_head *lh;
+
+ lh = ((struct list_head *)v)->next;
+ ++*ppos;
+ return lh == &vzprivnets ? NULL : lh;
+}
+
+static void sparse6_seq_stop(struct seq_file *s, void *v)
+{
+ read_unlock(&vzpriv6lock);
+}
+
+static int sparse6_seq_show(struct seq_file *s, void *v)
+{
+ struct vzprivnet *pn;
+ struct vzprivnet_entry *pne;
+ char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+
+ pn = list_entry(v, struct vzprivnet, list);
+ seq_printf(s, "%u: ", pn->netid);
+ if (pn->weak)
+ seq_puts(s, "* ");
+
+ list_for_each_entry(pne, &pn->entries, list) {
+ ip6_string(ip6_addr, (const char *)pne->ip),
+ seq_printf(s, "%s/%u ", ip6_addr, pne->preflen);
+ }
+
+ seq_putc(s, '\n');
+
+ return 0;
+}
+
+static struct seq_operations sparse6_seq_ops = {
+ .start = sparse6_seq_start,
+ .next = sparse6_seq_next,
+ .stop = sparse6_seq_stop,
+ .show = sparse6_seq_show,
+};
+
+static int sparse6_seq_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &sparse6_seq_ops);
+}
+
+static struct file_operations proc_sparse6_ops = {
+ .owner = THIS_MODULE,
+ .open = sparse6_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+ .write = sparse6_write,
+};
+
+static char sample_ipv6[42];
+
+static ssize_t classify6_write(struct file * file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int len;
+ char *tmp;
+
+ len = count;
+ if (len >= sizeof(sample_ipv6))
+ len = sizeof(sample_ipv6) - 1;
+
+ if (copy_from_user(sample_ipv6, buf, len))
+ return -EFAULT;
+
+ sample_ipv6[len] = '\0';
+ tmp = strchr(sample_ipv6, '\n');
+ if (tmp)
+ *tmp = '\0';
+
+ return count;
+}
+
+static int classify6_seq_show(struct seq_file *s, void *v)
+{
+ u32 ip[4];
+ struct vzprivnet_entry *pne;
+ char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+
+ seq_printf(s, "%s: ", sample_ipv6);
+
+ if (!in6_pton(sample_ipv6, ip, NULL)) {
+ seq_puts(s, "invalid IP\n");
+ return 0;
+ }
+
+ read_lock(&vzpriv6lock);
+ pne = vzprivnet6_lookup(ip);
+ if (pne == NULL) {
+ seq_printf(s, "internet\n");
+ goto out;
+ }
+
+ seq_printf(s, "net %u, ", pne->pn->netid);
+ ip6_string(ip6_addr, (const char *)pne->ip);
+ seq_printf(s, "rule %s/%u\n", ip6_addr, pne->preflen);
+out:
+ read_unlock(&vzpriv6lock);
+ return 0;
+}
+
+static int classify6_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &classify6_seq_show, NULL);
+}
+
+static struct file_operations proc_classify6_ops = {
+ .owner = THIS_MODULE,
+ .open = classify6_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+ .write = classify6_write,
+};
+
+static void vzprivnet6_show_stat(struct seq_file *f)
+{
+}
+
+static int __init ip6_vzprivnet_init(void)
+{
+ int err = -ENOMEM;
+ struct proc_dir_entry *proc;
+
+ proc = proc_create("sparse6", 0644,
+ vzpriv_proc_dir, &proc_sparse6_ops);
+ if (proc == NULL)
+ goto err_sparse6;
+
+ proc = proc_create("classify6", 0644,
+ vzpriv_proc_dir, &proc_classify6_ops);
+ if (proc == NULL)
+ goto err_classify6;
+
+ err = nf_register_hook(&vzprivnet6_ops);
+ if (err)
+ goto err_reg;
+
+ vzprivnet_reg_show(vzprivnet6_show_stat);
+ return 0;
+
+err_reg:
+ remove_proc_entry("classify6", vzpriv_proc_dir);
+err_classify6:
+ remove_proc_entry("sparse6", vzpriv_proc_dir);
+err_sparse6:
+ return err;
+}
+
+static void __exit ip6_vzprivnet_exit(void)
+{
+ vzprivnet_unreg_show(vzprivnet6_show_stat);
+ nf_unregister_hook(&vzprivnet6_ops);
+ remove_proc_entry("classify6", vzpriv_proc_dir);
+ remove_proc_entry("sparse6", vzpriv_proc_dir);
+ vzprivnet6_cleanup();
+}
+
+module_init(ip6_vzprivnet_init)
+module_exit(ip6_vzprivnet_exit)
More information about the Devel
mailing list