From 24d84abcc84ac1aa7be765d756fe3a311cadb920 Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Thu, 20 Mar 2014 16:31:55 +0100
Subject: [PATCH 07/18] hostapd: add freewifi eapsim support

---
 hostapd/config_file.c      |   41 ++++++++++++++++++++++++++++++++++++
 src/ap/ieee802_1x.c        |   50 ++++++++++++++++++++++++++++++++++++++++++++
 src/radius/radius.h        |    5 +++++
 src/radius/radius_client.h |    3 +++
 4 files changed, 99 insertions(+)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index e1f8b20..57efe3c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2162,6 +2162,47 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->radius->acct_server->shared_secret_len = len;
 	} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
 		bss->radius->retry_primary_interval = atoi(pos);
+	} else if (os_strcmp(buf, "radius_freewifi_gw") == 0) {
+		struct in_addr in;
+		char *p;
+		u32 bits;
+		u32 ip;
+
+		bits = 32;
+		if ((p = strchr(pos, '/'))) {
+			struct in_addr imask;
+
+			*p++ = 0;
+
+			/* netmask in address format or number
+			 * of bits */
+			if (inet_aton(p, &imask) == 0) {
+				bits = atoi(p);
+				if (bits > 32 || bits < 1) {
+					wpa_printf(MSG_ERROR,
+						   "Line %d: bad mask",
+						   line);
+					bits = 32;
+				}
+
+			} else {
+				u32 mask;
+
+				mask = ntohl(imask.s_addr);
+				bits = 33 - ffs(mask);
+			}
+		}
+
+		if (inet_aton(pos, &in) == 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: bad freewifi ip %s", line,
+				   pos);
+			return 1;
+		}
+		ip = in.s_addr;
+
+		bss->radius->freewifi_gw = ip;
+		bss->radius->freewifi_gw_count = 1 << (32 - bits);
 	} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
 		bss->acct_interim_interval = atoi(pos);
 	} else if (os_strcmp(buf, "radius_request_cui") == 0) {
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index b12c9d6..9f14ed5 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -558,6 +558,56 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_HS20 */
 
+	/* add private Freewifi AVP */
+	if (hapd->conf->radius->freewifi_gw) {
+		struct radius_attr_vendor *vhdr;
+		unsigned int hlen;
+		u8 *buf, *pos;
+		u8 last_byte;
+		u32 vendor_id = htonl(RADIUS_VENDOR_ID_FREEBOX);
+		struct in_addr in;
+		u32 gw;
+
+		hlen = sizeof(vendor_id) +  sizeof(*vhdr) + sizeof (gw);
+
+		buf = os_malloc(hlen);
+		if (!buf)
+			goto fail;
+
+		pos = buf;
+		os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+		pos += sizeof(vendor_id);
+
+		vhdr = (struct radius_attr_vendor *)pos;
+		vhdr->vendor_type = RADIUS_VENDOR_ATTR_FBX_FREEWIFI_GW;
+		vhdr->vendor_length = sizeof (*vhdr) + sizeof (gw);
+
+		/* compute freewifi gw from mac */
+		last_byte = sta->addr[5];
+		last_byte %= hapd->conf->radius->freewifi_gw_count;
+
+		gw = hapd->conf->radius->freewifi_gw;
+		gw = htonl(ntohl(gw) + last_byte);
+
+		pos = (u8 *) (vhdr + 1);
+		os_memcpy(pos, &gw, sizeof(gw));
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+					 buf, hlen)) {
+			wpa_printf(MSG_DEBUG, "Could not add Freewifi AVP");
+			os_free(buf);
+			goto fail;
+		}
+		os_free(buf);
+
+		in.s_addr = gw;
+		hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_INFO,
+			       "will send STA " MACSTR  " to gw %s",
+			       MAC2STR(sta->addr),
+			       inet_ntoa(in));
+	}
+
 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
 		goto fail;
 
diff --git a/src/radius/radius.h b/src/radius/radius.h
index d8bf21e..850ab82 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -175,6 +175,11 @@ enum {
 	RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
 };
 
+#define RADIUS_VENDOR_ID_FREEBOX 32456
+
+enum { RADIUS_VENDOR_ATTR_FBX_FREEWIFI_GW = 1,
+};
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 3db16aa..20af379 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -174,6 +174,9 @@ struct hostapd_radius_servers {
 	 * force_client_addr - Whether to force client (local) address
 	 */
 	int force_client_addr;
+
+	u32 freewifi_gw;
+	u32 freewifi_gw_count;
 };
 
 
-- 
1.7.9.5

