xref: /freebsd/contrib/wpa/src/ap/x_snoop.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
15b9c547cSRui Paulo /*
25b9c547cSRui Paulo  * Generic Snooping for Proxy ARP
35b9c547cSRui Paulo  * Copyright (c) 2014, Qualcomm Atheros, Inc.
45b9c547cSRui Paulo  *
55b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
65b9c547cSRui Paulo  * See README for more details.
75b9c547cSRui Paulo  */
85b9c547cSRui Paulo 
95b9c547cSRui Paulo #include "utils/includes.h"
105b9c547cSRui Paulo 
115b9c547cSRui Paulo #include "utils/common.h"
125b9c547cSRui Paulo #include "hostapd.h"
135b9c547cSRui Paulo #include "sta_info.h"
145b9c547cSRui Paulo #include "ap_drv_ops.h"
155b9c547cSRui Paulo #include "x_snoop.h"
165b9c547cSRui Paulo 
175b9c547cSRui Paulo 
x_snoop_init(struct hostapd_data * hapd)185b9c547cSRui Paulo int x_snoop_init(struct hostapd_data *hapd)
195b9c547cSRui Paulo {
205b9c547cSRui Paulo 	struct hostapd_bss_config *conf = hapd->conf;
215b9c547cSRui Paulo 
225b9c547cSRui Paulo 	if (!conf->isolate) {
235b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
245b9c547cSRui Paulo 			   "x_snoop: ap_isolate must be enabled for x_snoop");
255b9c547cSRui Paulo 		return -1;
265b9c547cSRui Paulo 	}
275b9c547cSRui Paulo 
285b9c547cSRui Paulo 	if (conf->bridge[0] == '\0') {
295b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
305b9c547cSRui Paulo 			   "x_snoop: Bridge must be configured for x_snoop");
315b9c547cSRui Paulo 		return -1;
325b9c547cSRui Paulo 	}
335b9c547cSRui Paulo 
34*a90b9d01SCy Schubert 	hapd->x_snoop_initialized = true;
35*a90b9d01SCy Schubert 
365b9c547cSRui Paulo 	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
375b9c547cSRui Paulo 					 1)) {
385b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
395b9c547cSRui Paulo 			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
405b9c547cSRui Paulo 		return -1;
415b9c547cSRui Paulo 	}
425b9c547cSRui Paulo 
435b9c547cSRui Paulo 	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
445b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
455b9c547cSRui Paulo 			   "x_snoop: Failed to enable proxyarp on the bridge port");
465b9c547cSRui Paulo 		return -1;
475b9c547cSRui Paulo 	}
485b9c547cSRui Paulo 
495b9c547cSRui Paulo 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
505b9c547cSRui Paulo 					 1)) {
515b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
525b9c547cSRui Paulo 			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
535b9c547cSRui Paulo 		return -1;
545b9c547cSRui Paulo 	}
555b9c547cSRui Paulo 
56325151a3SRui Paulo #ifdef CONFIG_IPV6
57325151a3SRui Paulo 	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
58325151a3SRui Paulo 		wpa_printf(MSG_DEBUG,
59325151a3SRui Paulo 			   "x_snoop: Failed to enable multicast snooping on the bridge");
60325151a3SRui Paulo 		return -1;
61325151a3SRui Paulo 	}
62325151a3SRui Paulo #endif /* CONFIG_IPV6 */
63325151a3SRui Paulo 
645b9c547cSRui Paulo 	return 0;
655b9c547cSRui Paulo }
665b9c547cSRui Paulo 
675b9c547cSRui Paulo 
685b9c547cSRui Paulo struct l2_packet_data *
x_snoop_get_l2_packet(struct hostapd_data * hapd,void (* handler)(void * ctx,const u8 * src_addr,const u8 * buf,size_t len),enum l2_packet_filter_type type)695b9c547cSRui Paulo x_snoop_get_l2_packet(struct hostapd_data *hapd,
705b9c547cSRui Paulo 		      void (*handler)(void *ctx, const u8 *src_addr,
715b9c547cSRui Paulo 				      const u8 *buf, size_t len),
725b9c547cSRui Paulo 		      enum l2_packet_filter_type type)
735b9c547cSRui Paulo {
745b9c547cSRui Paulo 	struct hostapd_bss_config *conf = hapd->conf;
755b9c547cSRui Paulo 	struct l2_packet_data *l2;
765b9c547cSRui Paulo 
775b9c547cSRui Paulo 	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
785b9c547cSRui Paulo 	if (l2 == NULL) {
795b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
805b9c547cSRui Paulo 			   "x_snoop: Failed to initialize L2 packet processing %s",
815b9c547cSRui Paulo 			   strerror(errno));
825b9c547cSRui Paulo 		return NULL;
835b9c547cSRui Paulo 	}
845b9c547cSRui Paulo 
855b9c547cSRui Paulo 	if (l2_packet_set_packet_filter(l2, type)) {
865b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
875b9c547cSRui Paulo 			   "x_snoop: Failed to set L2 packet filter for type: %d",
885b9c547cSRui Paulo 			   type);
895b9c547cSRui Paulo 		l2_packet_deinit(l2);
905b9c547cSRui Paulo 		return NULL;
915b9c547cSRui Paulo 	}
925b9c547cSRui Paulo 
935b9c547cSRui Paulo 	return l2;
945b9c547cSRui Paulo }
955b9c547cSRui Paulo 
965b9c547cSRui Paulo 
x_snoop_mcast_to_ucast_convert_send(struct hostapd_data * hapd,struct sta_info * sta,u8 * buf,size_t len)975b9c547cSRui Paulo void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
985b9c547cSRui Paulo 					 struct sta_info *sta, u8 *buf,
995b9c547cSRui Paulo 					 size_t len)
1005b9c547cSRui Paulo {
1015b9c547cSRui Paulo 	int res;
1025b9c547cSRui Paulo 	u8 addr[ETH_ALEN];
1035b9c547cSRui Paulo 	u8 *dst_addr = buf;
1045b9c547cSRui Paulo 
1055b9c547cSRui Paulo 	if (!(dst_addr[0] & 0x01))
1065b9c547cSRui Paulo 		return;
1075b9c547cSRui Paulo 
1085b9c547cSRui Paulo 	wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
1095b9c547cSRui Paulo 		   MACSTR " -> " MACSTR " (len %u)",
1105b9c547cSRui Paulo 		   MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
1115b9c547cSRui Paulo 
1125b9c547cSRui Paulo 	/* save the multicast destination address for restoring it later */
1135b9c547cSRui Paulo 	os_memcpy(addr, buf, ETH_ALEN);
1145b9c547cSRui Paulo 
1155b9c547cSRui Paulo 	os_memcpy(buf, sta->addr, ETH_ALEN);
1165b9c547cSRui Paulo 	res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
1175b9c547cSRui Paulo 	if (res < 0) {
1185b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
1195b9c547cSRui Paulo 			   "x_snoop: Failed to send mcast to ucast converted packet to "
1205b9c547cSRui Paulo 			   MACSTR, MAC2STR(sta->addr));
1215b9c547cSRui Paulo 	}
1225b9c547cSRui Paulo 
1235b9c547cSRui Paulo 	/* restore the multicast destination address */
1245b9c547cSRui Paulo 	os_memcpy(buf, addr, ETH_ALEN);
1255b9c547cSRui Paulo }
1265b9c547cSRui Paulo 
1275b9c547cSRui Paulo 
x_snoop_deinit(struct hostapd_data * hapd)1285b9c547cSRui Paulo void x_snoop_deinit(struct hostapd_data *hapd)
1295b9c547cSRui Paulo {
130*a90b9d01SCy Schubert 	if (!hapd->x_snoop_initialized)
131*a90b9d01SCy Schubert 		return;
1325b9c547cSRui Paulo 	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
1335b9c547cSRui Paulo 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
1345b9c547cSRui Paulo 	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
135*a90b9d01SCy Schubert 	hapd->x_snoop_initialized = false;
1365b9c547cSRui Paulo }
137