aboutsummaryrefslogtreecommitdiffstats
path: root/src/ap/x_snoop.c
blob: aef9a53c46cf90476592feee5d836ca82ae68379 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * Generic Snooping for Proxy ARP
 * Copyright (c) 2014, Qualcomm Atheros, Inc.
 *
 * This software may be distributed under the terms of the BSD license.
 * See README for more details.
 */

#include "utils/includes.h"

#include "utils/common.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_drv_ops.h"
#include "x_snoop.h"


int x_snoop_init(struct hostapd_data *hapd)
{
	struct hostapd_bss_config *conf = hapd->conf;

	if (!conf->isolate) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: ap_isolate must be enabled for x_snoop");
		return -1;
	}

	if (conf->bridge[0] == '\0') {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Bridge must be configured for x_snoop");
		return -1;
	}

	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
					 1)) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
		return -1;
	}

	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to enable proxyarp on the bridge port");
		return -1;
	}

	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
					 1)) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
		return -1;
	}

#ifdef CONFIG_IPV6
	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to enable multicast snooping on the bridge");
		return -1;
	}
#endif /* CONFIG_IPV6 */

	return 0;
}


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)
{
	struct hostapd_bss_config *conf = hapd->conf;
	struct l2_packet_data *l2;

	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
	if (l2 == NULL) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to initialize L2 packet processing %s",
			   strerror(errno));
		return NULL;
	}

	if (l2_packet_set_packet_filter(l2, type)) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to set L2 packet filter for type: %d",
			   type);
		l2_packet_deinit(l2);
		return NULL;
	}

	return l2;
}


void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
					 struct sta_info *sta, u8 *buf,
					 size_t len)
{
	int res;
	u8 addr[ETH_ALEN];
	u8 *dst_addr = buf;

	if (!(dst_addr[0] & 0x01))
		return;

	wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
		   MACSTR " -> " MACSTR " (len %u)",
		   MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);

	/* save the multicast destination address for restoring it later */
	os_memcpy(addr, buf, ETH_ALEN);

	os_memcpy(buf, sta->addr, ETH_ALEN);
	res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
	if (res < 0) {
		wpa_printf(MSG_DEBUG,
			   "x_snoop: Failed to send mcast to ucast converted packet to "
			   MACSTR, MAC2STR(sta->addr));
	}

	/* restore the multicast destination address */
	os_memcpy(buf, addr, ETH_ALEN);
}


void x_snoop_deinit(struct hostapd_data *hapd)
{
	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
}