From c4e0859d53d9626eb7be3805d3a796011eac1869 Mon Sep 17 00:00:00 2001
From: Kozlov Dmitry <xeb@mail.ru>
Date: Tue, 11 Jun 2013 15:47:51 +0400
Subject: ipoe: introduced 'gw-ip-address=x.x.x.x/mask' option to use it as
 server address and mask if radius can assign only client address

---
 accel-pppd/ctrl/ipoe/ipoe.c | 89 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 71 insertions(+), 18 deletions(-)

(limited to 'accel-pppd/ctrl/ipoe')

diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c
index b0d52013..18965d86 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.c
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -69,7 +69,7 @@ static const char *conf_lua_username_func;
 #endif
 
 static int conf_offer_timeout = 3;
-static in_addr_t conf_gw_address;
+static LIST_HEAD(conf_gw_addr);
 static int conf_netmask = 24;
 static int conf_lease_time = 600;
 static int conf_lease_timeout = 660;
@@ -83,33 +83,35 @@ static mempool_t ses_pool;
 
 static LIST_HEAD(serv_list);
 
-struct ifaddr
-{
+struct ifaddr {
 	struct list_head entry;
 	in_addr_t addr;
 	int refs;
 };
 
-struct iplink_arg
-{
+struct iplink_arg {
 	pcre *re;
 	const char *opt;
 };
 
-struct unit_cache
-{
+struct unit_cache {
 	struct list_head entry;
 	int ifindex;
 };
 
-struct l4_redirect
-{
+struct l4_redirect {
 	struct list_head entry;
 	int ifindex;
 	in_addr_t addr;
 	time_t timeout;
 };
 
+struct gw_addr {
+	struct list_head entry;
+	in_addr_t addr;
+	int mask;
+};
+
 static pthread_mutex_t uc_lock = PTHREAD_MUTEX_INITIALIZER;
 static LIST_HEAD(uc_list);
 static int uc_size;
@@ -472,6 +474,22 @@ static void ipoe_session_start(struct ipoe_session *ses)
 		__ipoe_session_start(ses);
 }
 
+static void find_gw_addr(struct ipoe_session *ses)
+{
+	struct gw_addr *a;
+
+	list_for_each_entry(a, &conf_gw_addr, entry) {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+		if ((ses->yiaddr & ((1<<a->mask) - 1)) == (a->addr & ((1<<a->mask) - 1))) {
+			ses->siaddr = a->addr;
+			ses->mask = a->mask;
+			return;
+		}
+#else
+#endif
+	}
+}
+
 static void __ipoe_session_start(struct ipoe_session *ses) 
 {
 	if (!ses->yiaddr) {
@@ -487,9 +505,6 @@ static void __ipoe_session_start(struct ipoe_session *ses)
 		ses->mask = conf_netmask;
 
 	if (ses->ses.ipv4) {
-		if (conf_gw_address)
-			ses->ses.ipv4->addr = conf_gw_address;
-		
 		if (!ses->mask)
 			ses->mask = ses->ses.ipv4->mask;
 
@@ -519,6 +534,9 @@ static void __ipoe_session_start(struct ipoe_session *ses)
 		if (!ses->siaddr && ses->serv->dhcpv4_relay)
 			ses->siaddr = ses->serv->dhcpv4_relay->giaddr;
 
+		if (!ses->siaddr)
+			find_gw_addr(ses);
+
 		if (!ses->siaddr) {
 			log_ppp_error("can't determine Server-ID\n");
 			ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0);
@@ -1855,6 +1873,46 @@ static void load_local_nets(struct conf_sect_t *sect)
 	}
 }
 
+static void load_gw_addr(struct conf_sect_t *sect)
+{
+	struct conf_option_t *opt;
+	struct gw_addr *a;
+	char addr[17];
+	char *ptr;
+
+	while (!list_empty(&conf_gw_addr)) {
+		a = list_entry(conf_gw_addr.next, typeof(*a), entry);
+		list_del(&a->entry);
+		_free(a);
+	}
+
+	list_for_each_entry(opt, &sect->items, entry) {
+		if (strcmp(opt->name, "gw-ip-address"))
+			continue;
+		if (!opt->val)
+			continue;
+
+		a = _malloc(sizeof(*a));
+		ptr = strchr(opt->val, '/');
+		if (ptr) {
+			memcpy(addr, opt->val, ptr - opt->val);
+			addr[ptr - opt->val] = 0;
+			a->addr = inet_addr(addr);
+			a->mask = atoi(ptr + 1);
+		} else {
+			a->addr = inet_addr(opt->val);
+			a->mask = 32;
+		}
+
+		if (a->addr == 0xffffffff || a->mask < 1 || a->mask > 32) {
+			log_error("ipoe: failed to parse '%s=%s'\n", opt->name, opt->val);
+			_free(a);
+			continue;
+		}
+		list_add_tail(&a->entry, &conf_gw_addr);
+	}
+}
+
 #ifdef RADIUS
 static void parse_conf_rad_attr(const char *opt, int *val)
 {
@@ -1908,12 +1966,6 @@ static void load_config(void)
 			log_emerg("ipoe: unknown username value '%s'\n", opt);
 	}
 
-	opt = conf_get_opt("ipoe", "gw-ip-address");
-	if (opt)
-		conf_gw_address = inet_addr(opt);
-	else
-		conf_gw_address = 0;
-
 	opt = conf_get_opt("ipoe", "netmask");
 	if (opt) {
 		conf_netmask = atoi(opt);
@@ -2027,6 +2079,7 @@ static void load_config(void)
 	
 	load_interfaces(s);
 	load_local_nets(s);
+	load_gw_addr(s);
 }
 
 static struct triton_context_t l4_redirect_ctx = {
-- 
cgit v1.2.3