diff mbox series

hostapd: Support SO_BINDTODEVICE for radius client.

Message ID 20200513204812.28542-1-greearb@candelatech.com
State Accepted
Headers show
Series hostapd: Support SO_BINDTODEVICE for radius client. | expand

Commit Message

Ben Greear May 13, 2020, 8:48 p.m. UTC
From: Ben Greear <greearb@candelatech.com>

This helps hostapd work better in VRF and other fancy
network environments.

Signed-off-by: Ben Greear <greearb@candelatech.com>
---
 hostapd/config_file.c      |  2 ++
 hostapd/hostapd.conf       |  5 +++++
 src/radius/radius_client.c | 20 ++++++++++++++++++++
 src/radius/radius_client.h |  5 +++++
 4 files changed, 32 insertions(+)

Comments

Andreas Tobler June 12, 2020, 11:08 a.m. UTC | #1
Hi Ben,

thanks for this patch. We applied it locally and with some modifications 
we got it to work.

The most important modification for us was to move the 'force disconnect 
part', the one which follows after the client-addr bind, before we do 
the SO_BINDTODEVICE.
If we do a connect with the disconnect_addr which has a sin_family 
AF_UNSPEC, we loose the SO_BINDTODEVICE on the socket.
This is the reason we moved it right before we bind the device.

The second modification is the usage of setsockopt(). Your's was not 
wrong but irritating while reading the first time. We just use the 
devicename and its length for the 4th and 5th argument instead of the irfeq.

Attached the updated patch with our modifications.

Cool thing!

Thanks,
Andreas

On 13.05.20 22:48, greearb@candelatech.com wrote:
> From: Ben Greear <greearb@candelatech.com>
> 
> This helps hostapd work better in VRF and other fancy
> network environments.
> 
> Signed-off-by: Ben Greear <greearb@candelatech.com>
> ---
>   hostapd/config_file.c      |  2 ++
>   hostapd/hostapd.conf       |  5 +++++
>   src/radius/radius_client.c | 20 ++++++++++++++++++++
>   src/radius/radius_client.h |  5 +++++
>   4 files changed, 32 insertions(+)
> 
> diff --git a/hostapd/config_file.c b/hostapd/config_file.c
> index 905402cf6..a2c8fbb79 100644
> --- a/hostapd/config_file.c
> +++ b/hostapd/config_file.c
> @@ -2727,6 +2727,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
>   			return 1;
>   		}
>   		bss->radius->force_client_addr = 1;
> +	} else if (os_strcmp(buf, "radius_client_dev") == 0) {
> +			bss->radius->force_client_dev = os_strdup(pos);
>   	} else if (os_strcmp(buf, "auth_server_addr") == 0) {
>   		if (hostapd_config_read_radius_addr(
>   			    &bss->radius->auth_servers,
> diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
> index 769a59ae8..b5719f8e7 100644
> --- a/hostapd/hostapd.conf
> +++ b/hostapd/hostapd.conf
> @@ -1312,6 +1312,11 @@ own_ip_addr=127.0.0.1
>   # used, e.g., when the device has multiple IP addresses.
>   #radius_client_addr=127.0.0.1
>   
> +# RADIUS client forced local interface.  Helps run properly with VRF
> +# Default is none set.
> +# Example below binds to eth0
> +#radius_client_dev=eth0
> +
>   # RADIUS authentication server
>   #auth_server_addr=127.0.0.1
>   #auth_server_port=1812
> diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
> index 2b7a604ed..a2ed9d410 100644
> --- a/src/radius/radius_client.c
> +++ b/src/radius/radius_client.c
> @@ -12,6 +12,7 @@
>   #include "radius.h"
>   #include "radius_client.h"
>   #include "eloop.h"
> +#include <net/if.h>
>   
>   /* Defaults for RADIUS retransmit values (exponential backoff) */
>   
> @@ -1159,6 +1160,25 @@ radius_change_server(struct radius_client_data *radius,
>   		return -1;
>   	}
>   
> +#ifdef __linux__
> +	if (conf->force_client_dev && conf->force_client_dev[0]) {
> +		struct ifreq ifr;
> +		os_memset(&ifr, 0, sizeof(ifr));
> +		os_strlcpy(ifr.ifr_ifrn.ifrn_name, conf->force_client_dev, IFNAMSIZ);
> +		if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
> +			       (char *) &ifr, sizeof(ifr)) < 0) {
> +			wpa_printf(MSG_ERROR,
> +				   "setsockopt[RADIUS CLIENT, SO_BINDTODEVICE]: %s",
> +				   strerror(errno));
> +			/* probably not critical error, continue on and hope for the best. */
> +		}
> +		else {
> +			wpa_printf(MSG_ERROR,
> +				   "Bound radius client socket to device: %s", conf->force_client_dev);
> +		}
> +	}
> +#endif
> +
>   	if (conf->force_client_addr) {
>   		switch (conf->client_addr.af) {
>   		case AF_INET:
> diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
> index 8ca0874db..4d050545b 100644
> --- a/src/radius/radius_client.h
> +++ b/src/radius/radius_client.h
> @@ -174,6 +174,11 @@ struct hostapd_radius_servers {
>   	 * force_client_addr - Whether to force client (local) address
>   	 */
>   	int force_client_addr;
> +
> +	/**
> +	 * Will call SO_BINDTODEVICE on this if non null and non-empty string.
> +	 */
> +	char* force_client_dev;
>   };
>   
>   
>
From 810efc8ae1c27064fa800328c6c924326cf8a162 Mon Sep 17 00:00:00 2001
From: Ben Greear <greearb at candelatech.com>
Date: Wed, 13 May 2020 13:48:12 -0700
Subject: [PATCH] hostapd: Support SO_BINDTODEVICE for radius client.

This helps hostapd work better in VRF and other fancy
network environments.

Signed-off-by: Ben Greear <greearb at candelatech.com>
Signed-off-by: Andreas Tobler <andreas.tobler at onway.ch>
---
 hostapd/config_file.c      |  2 ++
 hostapd/hostapd.conf       |  5 +++++
 src/radius/radius_client.c | 28 +++++++++++++++++++++++-----
 src/radius/radius_client.h |  5 +++++
 4 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 8f7fcd8b7..76f695804 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2677,6 +2677,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 			return 1;
 		}
 		bss->radius->force_client_addr = 1;
+	} else if (os_strcmp(buf, "radius_client_dev") == 0) {
+			bss->radius->force_client_dev = os_strdup(pos);
 	} else if (os_strcmp(buf, "auth_server_addr") == 0) {
 		if (hostapd_config_read_radius_addr(
 			    &bss->radius->auth_servers,
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 2b0f762e5..895d13290 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1305,6 +1305,11 @@ own_ip_addr=127.0.0.1
 # used, e.g., when the device has multiple IP addresses.
 #radius_client_addr=127.0.0.1
 
+# RADIUS client forced local interface.  Helps run properly with VRF
+# Default is none set.
+# Example below binds to eth0
+#radius_client_dev=eth0
+
 # RADIUS authentication server
 #auth_server_addr=127.0.0.1
 #auth_server_port=1812
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 2b7a604ed..dc685b288 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -12,6 +12,7 @@
 #include "radius.h"
 #include "radius_client.h"
 #include "eloop.h"
+#include <net/if.h>
 
 /* Defaults for RADIUS retransmit values (exponential backoff) */
 
@@ -1159,6 +1160,28 @@ radius_change_server(struct radius_client_data *radius,
 		return -1;
 	}
 
+	/* Force a reconnect by disconnecting the socket first */
+	if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
+		    sizeof(disconnect_addr)) < 0)
+		wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+
+#ifdef __linux__
+	if (conf->force_client_dev && conf->force_client_dev[0]) {
+		if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
+			       conf->force_client_dev,
+			       strlen(conf->force_client_dev)) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "setsockopt[RADIUS CLIENT, SO_BINDTODEVICE]: %s",
+				   strerror(errno));
+			/* probably not critical error, continue on and hope for the best. */
+		}
+		else {
+			wpa_printf(MSG_ERROR,
+				   "Bound radius client socket to device: %s", conf->force_client_dev);
+		}
+	}
+#endif
+
 	if (conf->force_client_addr) {
 		switch (conf->client_addr.af) {
 		case AF_INET:
@@ -1191,11 +1214,6 @@ radius_change_server(struct radius_client_data *radius,
 		}
 	}
 
-	/* Force a reconnect by disconnecting the socket first */
-	if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
-		    sizeof(disconnect_addr)) < 0)
-		wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
-
 	if (connect(sel_sock, addr, addrlen) < 0) {
 		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
 		return -1;
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 8ca0874db..4d050545b 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -174,6 +174,11 @@ struct hostapd_radius_servers {
 	 * force_client_addr - Whether to force client (local) address
 	 */
 	int force_client_addr;
+
+	/**
+	 * Will call SO_BINDTODEVICE on this if non null and non-empty string.
+	 */
+	char* force_client_dev;
 };
Jouni Malinen Feb. 27, 2021, 9:43 a.m. UTC | #2
On Fri, Jun 12, 2020 at 01:08:32PM +0200, Andreas Tobler wrote:
> thanks for this patch. We applied it locally and with some modifications we
> got it to work.
> 
> The most important modification for us was to move the 'force disconnect
> part', the one which follows after the client-addr bind, before we do the
> SO_BINDTODEVICE.
> If we do a connect with the disconnect_addr which has a sin_family
> AF_UNSPEC, we loose the SO_BINDTODEVICE on the socket.
> This is the reason we moved it right before we bind the device.
> 
> The second modification is the usage of setsockopt(). Your's was not wrong
> but irritating while reading the first time. We just use the devicename and
> its length for the 4th and 5th argument instead of the irfeq.
> 
> Attached the updated patch with our modifications.

Thanks, applied with fixes and cleanup.
diff mbox series

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 905402cf6..a2c8fbb79 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2727,6 +2727,8 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 			return 1;
 		}
 		bss->radius->force_client_addr = 1;
+	} else if (os_strcmp(buf, "radius_client_dev") == 0) {
+			bss->radius->force_client_dev = os_strdup(pos);
 	} else if (os_strcmp(buf, "auth_server_addr") == 0) {
 		if (hostapd_config_read_radius_addr(
 			    &bss->radius->auth_servers,
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 769a59ae8..b5719f8e7 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1312,6 +1312,11 @@  own_ip_addr=127.0.0.1
 # used, e.g., when the device has multiple IP addresses.
 #radius_client_addr=127.0.0.1
 
+# RADIUS client forced local interface.  Helps run properly with VRF
+# Default is none set.
+# Example below binds to eth0
+#radius_client_dev=eth0
+
 # RADIUS authentication server
 #auth_server_addr=127.0.0.1
 #auth_server_port=1812
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 2b7a604ed..a2ed9d410 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -12,6 +12,7 @@ 
 #include "radius.h"
 #include "radius_client.h"
 #include "eloop.h"
+#include <net/if.h>
 
 /* Defaults for RADIUS retransmit values (exponential backoff) */
 
@@ -1159,6 +1160,25 @@  radius_change_server(struct radius_client_data *radius,
 		return -1;
 	}
 
+#ifdef __linux__
+	if (conf->force_client_dev && conf->force_client_dev[0]) {
+		struct ifreq ifr;
+		os_memset(&ifr, 0, sizeof(ifr));
+		os_strlcpy(ifr.ifr_ifrn.ifrn_name, conf->force_client_dev, IFNAMSIZ);
+		if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
+			       (char *) &ifr, sizeof(ifr)) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "setsockopt[RADIUS CLIENT, SO_BINDTODEVICE]: %s",
+				   strerror(errno));
+			/* probably not critical error, continue on and hope for the best. */
+		}
+		else {
+			wpa_printf(MSG_ERROR,
+				   "Bound radius client socket to device: %s", conf->force_client_dev);
+		}
+	}
+#endif
+
 	if (conf->force_client_addr) {
 		switch (conf->client_addr.af) {
 		case AF_INET:
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 8ca0874db..4d050545b 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -174,6 +174,11 @@  struct hostapd_radius_servers {
 	 * force_client_addr - Whether to force client (local) address
 	 */
 	int force_client_addr;
+
+	/**
+	 * Will call SO_BINDTODEVICE on this if non null and non-empty string.
+	 */
+	char* force_client_dev;
 };