From d9e66279652a9e6639d5a82581bcc0fd7d999aff Mon Sep 17 00:00:00 2001
From: Dmitry Kozlov <xeb@mail.ru>
Date: Fri, 18 Mar 2016 15:22:55 +0300
Subject: ipoe: restored local-net function

local-net is used to filter incomming packets which may start UP session
---
 accel-pppd/accel-ppp.conf           |  1 +
 accel-pppd/accel-ppp.conf.5         |  3 ++
 accel-pppd/ctrl/ipoe/ipoe.c         | 83 ++++++++++++++++++++++++++++++++
 accel-pppd/ctrl/ipoe/ipoe.h         |  2 +
 accel-pppd/ctrl/ipoe/ipoe_netlink.c | 71 +++++++++++++++++++++++++++
 drivers/ipoe/ipoe.c                 | 96 +++++++++++++++++++++++++++++++++++--
 drivers/ipoe/ipoe.h                 |  2 +
 7 files changed, 253 insertions(+), 5 deletions(-)

diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index bd58a7a7..6b6352e0 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -143,6 +143,7 @@ start=dhcpv4
 #soft-terminate=0
 #check-mac-change=1
 #calling-sid=mac
+#local-net=192.168.0.0/16
 interface=eth0
 
 
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index 4deec4e1..648fd247 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -354,6 +354,9 @@ The
 .B proxy-arp
 parameter specifies whether accel-ppp should reply to arp requests.
 .TP
+.BI "local-net=" x.x.x.x/mask
+Specifies networks from which packets will be treated as unclassified. You may specify multiple local-net options.
+.TP
 .BI "proto=" n
 Specifies number of protocol to be used for inserted routes.
 .TP
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c
index 63df0660..49e32a7e 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.c
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -89,6 +89,13 @@ struct request_item {
 	int cnt;
 };
 
+struct local_net {
+	struct list_head entry;
+	in_addr_t addr;
+	int mask;
+	int active;
+};
+
 enum {SID_MAC, SID_IP};
 
 static int conf_dhcpv4 = 1;
@@ -171,6 +178,8 @@ static LIST_HEAD(uc_list);
 static int uc_size;
 static mempool_t uc_pool;
 
+static LIST_HEAD(local_nets);
+
 static pthread_rwlock_t l4_list_lock = PTHREAD_RWLOCK_INITIALIZER;
 static LIST_HEAD(l4_redirect_list);
 static struct triton_timer_t l4_redirect_timer;
@@ -3019,6 +3028,79 @@ static void load_vlan_mon(struct conf_sect_t *sect)
 	}
 }
 
+static void parse_local_net(const char *opt)
+{
+	const char *ptr;
+	char str[17];
+	in_addr_t addr;
+	int mask;
+	char *endptr;
+	struct local_net *n;
+
+	ptr = strchr(opt, '/');
+	if (ptr) {
+		memcpy(str, opt, ptr - opt);
+		str[ptr - opt] = 0;
+		addr = inet_addr(str);
+		if (addr == INADDR_NONE)
+			goto out_err;
+		mask = strtoul(ptr + 1, &endptr, 10);
+		if (mask > 32)
+			goto out_err;
+	} else {
+		addr = inet_addr(opt);
+		if (addr == INADDR_NONE)
+			goto out_err;
+		mask = 24;
+	}
+
+	list_for_each_entry(n, &local_nets, entry) {
+		if (n->addr == addr && n->mask == mask) {
+			n->active = 1;
+			return;
+		}
+	}
+
+	n = _malloc(sizeof(*n));
+	n->addr = addr;
+	n->mask = mask;
+	n->active = 1;
+	list_add_tail(&n->entry, &local_nets);
+
+	ipoe_nl_add_net(addr, mask);
+
+	return;
+
+out_err:
+	log_error("ipoe: failed to parse 'local-net=%s'\n", opt);
+}
+
+static void load_local_nets(struct conf_sect_t *sect)
+{
+	struct conf_option_t *opt;
+	struct local_net *n;
+	struct list_head *pos, *t;
+
+	list_for_each_entry(n, &local_nets, entry)
+		n->active = 0;
+
+	list_for_each_entry(opt, &sect->items, entry) {
+		if (strcmp(opt->name, "local-net"))
+			continue;
+		if (!opt->val)
+			continue;
+		parse_local_net(opt->val);
+	}
+
+	list_for_each_safe(pos, t, &local_nets) {
+		n = list_entry(pos, typeof(*n), entry);
+		if (!n->active) {
+			ipoe_nl_del_net(n->addr);
+			list_del(&n->entry);
+			_free(n);
+		}
+	}
+}
 
 static void load_config(void)
 {
@@ -3280,6 +3362,7 @@ static void load_config(void)
 	load_interfaces(s);
 	load_vlan_mon(s);
 	load_gw_addr(s);
+	load_local_nets(s);
 }
 
 static struct triton_context_t l4_redirect_ctx = {
diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h
index 5561646d..825ffcaa 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.h
+++ b/accel-pppd/ctrl/ipoe/ipoe.h
@@ -128,6 +128,8 @@ int ipoe_nl_modify(int ifindex, uint32_t peer_addr, uint32_t addr, uint32_t gw,
 void ipoe_nl_get_sessions(struct list_head *list);
 int ipoe_nl_add_exclude(uint32_t addr, int mask);
 void ipoe_nl_del_exclude(uint32_t addr);
+int ipoe_nl_add_net(uint32_t addr, int mask);
+void ipoe_nl_del_net(uint32_t addr);
 
 void *arpd_start(struct ipoe_serv *ipoe);
 void arpd_stop(void *arp);
diff --git a/accel-pppd/ctrl/ipoe/ipoe_netlink.c b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
index 7e61fb84..9372a94c 100644
--- a/accel-pppd/ctrl/ipoe/ipoe_netlink.c
+++ b/accel-pppd/ctrl/ipoe/ipoe_netlink.c
@@ -98,6 +98,76 @@ void ipoe_nl_del_exclude(uint32_t addr)
 	rtnl_close(&rth);
 }
 
+int ipoe_nl_add_net(uint32_t addr, int mask)
+{
+	struct rtnl_handle rth;
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *ghdr;
+	struct {
+		struct nlmsghdr n;
+		char buf[1024];
+	} req;
+	int ret = 0;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
+		log_ppp_error("ipoe: cannot open generic netlink socket\n");
+		return -1;
+	}
+
+	nlh = &req.n;
+	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh->nlmsg_type = ipoe_genl_id;
+
+	ghdr = NLMSG_DATA(&req.n);
+	ghdr->cmd = IPOE_CMD_ADD_NET;
+
+	mask = mask ? ~0 << (32 - mask) : 0;
+
+	addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
+	addattr32(nlh, 1024, IPOE_ATTR_MASK, mask);
+
+	if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) {
+		log_ppp_error("ipoe: nl_add_net: error talking to kernel\n");
+		ret = -1;
+	}
+
+	rtnl_close(&rth);
+
+	return ret;
+}
+
+void ipoe_nl_del_net(uint32_t addr)
+{
+	struct rtnl_handle rth;
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *ghdr;
+	struct {
+		struct nlmsghdr n;
+		char buf[1024];
+	} req;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
+		log_ppp_error("ipoe: cannot open generic netlink socket\n");
+		return;
+	}
+
+	nlh = &req.n;
+	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	nlh->nlmsg_type = ipoe_genl_id;
+
+	ghdr = NLMSG_DATA(&req.n);
+	ghdr->cmd = IPOE_CMD_DEL_NET;
+
+	addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
+
+	if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
+		log_ppp_error("ipoe: nl_del_net: error talking to kernel\n");
+
+	rtnl_close(&rth);
+}
+
 void ipoe_nl_add_interface(int ifindex, uint8_t mode)
 {
 	struct rtnl_handle rth;
@@ -544,6 +614,7 @@ static void init(void)
 	triton_context_wakeup(&mc_ctx);
 
 	ipoe_nl_del_exclude(0);
+	ipoe_nl_del_net(0);
 	ipoe_nl_delete_interfaces();
 }
 
diff --git a/drivers/ipoe/ipoe.c b/drivers/ipoe/ipoe.c
index 182b3d98..7eccf6e1 100644
--- a/drivers/ipoe/ipoe.c
+++ b/drivers/ipoe/ipoe.c
@@ -108,6 +108,7 @@ static LIST_HEAD(ipoe_list2);
 static LIST_HEAD(ipoe_list2_u);
 static DEFINE_SEMAPHORE(ipoe_wlock);
 static LIST_HEAD(ipoe_interfaces);
+static LIST_HEAD(ipoe_networks);
 static struct work_struct ipoe_queue_work;
 static struct sk_buff_head ipoe_queue;
 
@@ -159,6 +160,29 @@ static void ipoe_update_stats(struct sk_buff *skb, struct ipoe_stats *st, int co
 	u64_stats_update_end(&st->sync);
 }
 
+static int ipoe_check_network(__be32 addr)
+{
+	struct ipoe_network *n;
+	int r;
+
+	if (list_empty(&ipoe_networks))
+		return 1;
+
+	r = 0;
+	addr = ntohl(addr);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(n, &ipoe_networks, entry) {
+		if ((addr & n->mask) == n->addr) {
+			r = 1;
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return r;
+}
+
 static int ipoe_check_exclude(__be32 addr)
 {
 	struct ipoe_network *n;
@@ -689,8 +713,11 @@ static rx_handler_result_t ipoe_recv(struct sk_buff **pskb)
 		if (ipoe_check_exclude(iph->saddr))
 			return RX_HANDLER_PASS;
 
-		if (ipoe_queue_u(skb, iph->saddr))
-			kfree_skb(skb);
+		if (ipoe_check_network(iph->saddr)) {
+			if (ipoe_queue_u(skb, iph->saddr))
+				kfree_skb(skb);
+		} else
+			return RX_HANDLER_PASS;
 
 		return RX_HANDLER_CONSUMED;
 	}
@@ -1369,13 +1396,12 @@ static int ipoe_nl_cmd_del_exclude(struct sk_buff *skb, struct genl_info *info)
 		if (n->addr == addr) {
 			list_del_rcu(&n->entry);
 			kfree_rcu(n, rcu_head);
+			break;
 		}
 	}
 	rcu_read_unlock();
 	up(&ipoe_wlock);
 
-	synchronize_rcu();
-
 	return 0;
 }
 
@@ -1444,7 +1470,7 @@ static int ipoe_nl_cmd_del_interface(struct sk_buff *skb, struct genl_info *info
 		if (ifindex == -1 || ifindex == i->ifindex) {
 			dev = __dev_get_by_index(&init_net, i->ifindex);
 
-			if (dev)
+			if (dev && rcu_dereference(dev->rx_handler) == ipoe_recv)
 				netdev_rx_handler_unregister(dev);
 
 			list_del(&i->entry);
@@ -1460,6 +1486,54 @@ static int ipoe_nl_cmd_del_interface(struct sk_buff *skb, struct genl_info *info
 	return 0;
 }
 
+static int ipoe_nl_cmd_add_net(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ipoe_network *n;
+
+	if (!info->attrs[IPOE_ATTR_ADDR] || !info->attrs[IPOE_ATTR_MASK])
+		return -EINVAL;
+
+	n = kmalloc(sizeof(*n), GFP_KERNEL);
+	if (!n)
+		return -ENOMEM;
+
+	n->addr = nla_get_u32(info->attrs[IPOE_ATTR_ADDR]);
+	n->mask = nla_get_u32(info->attrs[IPOE_ATTR_MASK]);
+	n->addr = ntohl(n->addr) & n->mask;
+
+	down(&ipoe_wlock);
+	list_add_tail_rcu(&n->entry, &ipoe_networks);
+	up(&ipoe_wlock);
+
+	return 0;
+}
+
+static int ipoe_nl_cmd_del_net(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ipoe_network *n;
+	__be32 addr;
+
+	if (!info->attrs[IPOE_ATTR_ADDR])
+		return -EINVAL;
+
+	addr = ntohl(nla_get_u32(info->attrs[IPOE_ATTR_ADDR]));
+
+	down(&ipoe_wlock);
+	rcu_read_lock();
+	list_for_each_entry_rcu(n, &ipoe_networks, entry) {
+		if (!addr || (addr & n->mask) == n->addr) {
+			list_del_rcu(&n->entry);
+			kfree_rcu(n, rcu_head);
+			if (addr)
+				break;
+		}
+	}
+	rcu_read_unlock();
+	up(&ipoe_wlock);
+
+	return 0;
+}
+
 static struct nla_policy ipoe_nl_policy[IPOE_ATTR_MAX + 1] = {
 	[IPOE_ATTR_NONE]		    = { .type = NLA_UNSPEC,                     },
 	[IPOE_ATTR_ADDR]	      = { .type = NLA_U32,                        },
@@ -1526,6 +1600,18 @@ static struct genl_ops ipoe_nl_ops[] = {
 		.policy = ipoe_nl_policy,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = IPOE_CMD_ADD_NET,
+		.doit = ipoe_nl_cmd_add_net,
+		.policy = ipoe_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = IPOE_CMD_DEL_NET,
+		.doit = ipoe_nl_cmd_del_net,
+		.policy = ipoe_nl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family ipoe_nl_family = {
diff --git a/drivers/ipoe/ipoe.h b/drivers/ipoe/ipoe.h
index c37cb73e..c735c2cf 100644
--- a/drivers/ipoe/ipoe.h
+++ b/drivers/ipoe/ipoe.h
@@ -14,6 +14,8 @@ enum {
 	IPOE_REP_PKT,
 	IPOE_CMD_ADD_EXCLUDE,
 	IPOE_CMD_DEL_EXCLUDE,
+	IPOE_CMD_ADD_NET,
+	IPOE_CMD_DEL_NET,
 	__IPOE_CMD_MAX,
 };
 
-- 
cgit v1.2.3