1209139Srpaulo/*
2209139Srpaulo * WPA Supplicant - Layer2 packet handling with FreeBSD
3209139Srpaulo * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
4209139Srpaulo * Copyright (c) 2005, Sam Leffler <sam@errno.com>
5209139Srpaulo *
6252726Srpaulo * This software may be distributed under the terms of the BSD license.
7252726Srpaulo * See README for more details.
8209139Srpaulo */
9209139Srpaulo
10209139Srpaulo#include "includes.h"
11214734Srpaulo#if defined(__APPLE__) || defined(__GLIBC__)
12209139Srpaulo#include <net/bpf.h>
13209139Srpaulo#endif /* __APPLE__ */
14209139Srpaulo#include <pcap.h>
15209139Srpaulo
16209139Srpaulo#include <sys/ioctl.h>
17252726Srpaulo#ifdef __sun__
18252726Srpaulo#include <libdlpi.h>
19252726Srpaulo#else /* __sun__ */
20209139Srpaulo#include <sys/sysctl.h>
21252726Srpaulo#endif /* __sun__ */
22209139Srpaulo
23209139Srpaulo#include <net/if.h>
24209139Srpaulo#include <net/if_dl.h>
25209139Srpaulo#include <net/route.h>
26209139Srpaulo#include <netinet/in.h>
27209139Srpaulo
28209139Srpaulo#include "common.h"
29209139Srpaulo#include "eloop.h"
30209139Srpaulo#include "l2_packet.h"
31209139Srpaulo
32209139Srpaulo
33209139Srpaulostatic const u8 pae_group_addr[ETH_ALEN] =
34209139Srpaulo{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
35209139Srpaulo
36209139Srpaulostruct l2_packet_data {
37209139Srpaulo	pcap_t *pcap;
38209139Srpaulo	char ifname[100];
39209139Srpaulo	u8 own_addr[ETH_ALEN];
40209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
41209139Srpaulo			    const u8 *buf, size_t len);
42209139Srpaulo	void *rx_callback_ctx;
43209139Srpaulo	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
44209139Srpaulo		     * buffers */
45209139Srpaulo};
46209139Srpaulo
47209139Srpaulo
48209139Srpauloint l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
49209139Srpaulo{
50209139Srpaulo	os_memcpy(addr, l2->own_addr, ETH_ALEN);
51209139Srpaulo	return 0;
52209139Srpaulo}
53209139Srpaulo
54209139Srpaulo
55209139Srpauloint l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
56209139Srpaulo		   const u8 *buf, size_t len)
57209139Srpaulo{
58209139Srpaulo	if (!l2->l2_hdr) {
59209139Srpaulo		int ret;
60209139Srpaulo		struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
61209139Srpaulo		if (eth == NULL)
62209139Srpaulo			return -1;
63209139Srpaulo		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
64209139Srpaulo		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
65209139Srpaulo		eth->h_proto = htons(proto);
66209139Srpaulo		os_memcpy(eth + 1, buf, len);
67209139Srpaulo		ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
68209139Srpaulo		os_free(eth);
69209139Srpaulo		return ret;
70209139Srpaulo	} else
71209139Srpaulo		return pcap_inject(l2->pcap, buf, len);
72209139Srpaulo}
73209139Srpaulo
74209139Srpaulo
75209139Srpaulostatic void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
76209139Srpaulo{
77209139Srpaulo	struct l2_packet_data *l2 = eloop_ctx;
78209139Srpaulo	pcap_t *pcap = sock_ctx;
79209139Srpaulo	struct pcap_pkthdr hdr;
80209139Srpaulo	const u_char *packet;
81209139Srpaulo	struct l2_ethhdr *ethhdr;
82209139Srpaulo	unsigned char *buf;
83209139Srpaulo	size_t len;
84209139Srpaulo
85209139Srpaulo	packet = pcap_next(pcap, &hdr);
86209139Srpaulo
87209139Srpaulo	if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
88209139Srpaulo		return;
89209139Srpaulo
90209139Srpaulo	ethhdr = (struct l2_ethhdr *) packet;
91209139Srpaulo	if (l2->l2_hdr) {
92209139Srpaulo		buf = (unsigned char *) ethhdr;
93209139Srpaulo		len = hdr.caplen;
94209139Srpaulo	} else {
95209139Srpaulo		buf = (unsigned char *) (ethhdr + 1);
96209139Srpaulo		len = hdr.caplen - sizeof(*ethhdr);
97209139Srpaulo	}
98209139Srpaulo	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
99209139Srpaulo}
100209139Srpaulo
101209139Srpaulo
102209139Srpaulostatic int l2_packet_init_libpcap(struct l2_packet_data *l2,
103209139Srpaulo				  unsigned short protocol)
104209139Srpaulo{
105209139Srpaulo	bpf_u_int32 pcap_maskp, pcap_netp;
106209139Srpaulo	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
107209139Srpaulo	struct bpf_program pcap_fp;
108209139Srpaulo
109209139Srpaulo	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
110209139Srpaulo	l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
111209139Srpaulo	if (l2->pcap == NULL) {
112209139Srpaulo		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
113209139Srpaulo		fprintf(stderr, "ifname='%s'\n", l2->ifname);
114209139Srpaulo		return -1;
115209139Srpaulo	}
116209139Srpaulo	if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
117209139Srpaulo	    pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
118209139Srpaulo		fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
119209139Srpaulo			pcap_geterr(l2->pcap));
120209139Srpaulo		return -1;
121209139Srpaulo	}
122209139Srpaulo	os_snprintf(pcap_filter, sizeof(pcap_filter),
123209139Srpaulo		    "not ether src " MACSTR " and "
124209139Srpaulo		    "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
125209139Srpaulo		    "ether proto 0x%x",
126209139Srpaulo		    MAC2STR(l2->own_addr), /* do not receive own packets */
127209139Srpaulo		    MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
128209139Srpaulo		    protocol);
129209139Srpaulo	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
130209139Srpaulo		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
131209139Srpaulo		return -1;
132209139Srpaulo	}
133209139Srpaulo
134209139Srpaulo	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
135209139Srpaulo		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
136209139Srpaulo		return -1;
137209139Srpaulo	}
138209139Srpaulo
139209139Srpaulo	pcap_freecode(&pcap_fp);
140252726Srpaulo#ifndef __sun__
141209139Srpaulo	/*
142209139Srpaulo	 * When libpcap uses BPF we must enable "immediate mode" to
143209139Srpaulo	 * receive frames right away; otherwise the system may
144209139Srpaulo	 * buffer them for us.
145209139Srpaulo	 */
146209139Srpaulo	{
147209139Srpaulo		unsigned int on = 1;
148209139Srpaulo		if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
149209139Srpaulo			fprintf(stderr, "%s: cannot enable immediate mode on "
150209139Srpaulo				"interface %s: %s\n",
151209139Srpaulo				__func__, l2->ifname, strerror(errno));
152209139Srpaulo			/* XXX should we fail? */
153209139Srpaulo		}
154209139Srpaulo	}
155252726Srpaulo#endif /* __sun__ */
156209139Srpaulo
157209139Srpaulo	eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
158209139Srpaulo				 l2_packet_receive, l2, l2->pcap);
159209139Srpaulo
160209139Srpaulo	return 0;
161209139Srpaulo}
162209139Srpaulo
163209139Srpaulo
164209139Srpaulostatic int eth_get(const char *device, u8 ea[ETH_ALEN])
165209139Srpaulo{
166252726Srpaulo#ifdef __sun__
167252726Srpaulo	dlpi_handle_t dh;
168252726Srpaulo	u32 physaddrlen = DLPI_PHYSADDR_MAX;
169252726Srpaulo	u8 physaddr[DLPI_PHYSADDR_MAX];
170252726Srpaulo	int retval;
171252726Srpaulo
172252726Srpaulo	retval = dlpi_open(device, &dh, 0);
173252726Srpaulo	if (retval != DLPI_SUCCESS) {
174252726Srpaulo		wpa_printf(MSG_ERROR, "dlpi_open error: %s",
175252726Srpaulo			   dlpi_strerror(retval));
176252726Srpaulo		return -1;
177252726Srpaulo	}
178252726Srpaulo
179252726Srpaulo	retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
180252726Srpaulo				   &physaddrlen);
181252726Srpaulo	if (retval != DLPI_SUCCESS) {
182252726Srpaulo		wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
183252726Srpaulo			   dlpi_strerror(retval));
184252726Srpaulo		dlpi_close(dh);
185252726Srpaulo		return -1;
186252726Srpaulo	}
187252726Srpaulo	os_memcpy(ea, physaddr, ETH_ALEN);
188252726Srpaulo	dlpi_close(dh);
189252726Srpaulo#else /* __sun__ */
190209139Srpaulo	struct if_msghdr *ifm;
191209139Srpaulo	struct sockaddr_dl *sdl;
192209139Srpaulo	u_char *p, *buf;
193209139Srpaulo	size_t len;
194209139Srpaulo	int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
195209139Srpaulo
196209139Srpaulo	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
197209139Srpaulo		return -1;
198209139Srpaulo	if ((buf = os_malloc(len)) == NULL)
199209139Srpaulo		return -1;
200209139Srpaulo	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
201209139Srpaulo		os_free(buf);
202209139Srpaulo		return -1;
203209139Srpaulo	}
204209139Srpaulo	for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
205209139Srpaulo		ifm = (struct if_msghdr *)p;
206209139Srpaulo		sdl = (struct sockaddr_dl *)(ifm + 1);
207209139Srpaulo		if (ifm->ifm_type != RTM_IFINFO ||
208209139Srpaulo		    (ifm->ifm_addrs & RTA_IFP) == 0)
209209139Srpaulo			continue;
210209139Srpaulo		if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
211209139Srpaulo		    os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
212209139Srpaulo			continue;
213209139Srpaulo		os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
214209139Srpaulo		break;
215209139Srpaulo	}
216209139Srpaulo	os_free(buf);
217209139Srpaulo
218209139Srpaulo	if (p >= buf + len) {
219209139Srpaulo		errno = ESRCH;
220209139Srpaulo		return -1;
221209139Srpaulo	}
222252726Srpaulo#endif /* __sun__ */
223209139Srpaulo	return 0;
224209139Srpaulo}
225209139Srpaulo
226209139Srpaulo
227209139Srpaulostruct l2_packet_data * l2_packet_init(
228209139Srpaulo	const char *ifname, const u8 *own_addr, unsigned short protocol,
229209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
230209139Srpaulo			    const u8 *buf, size_t len),
231209139Srpaulo	void *rx_callback_ctx, int l2_hdr)
232209139Srpaulo{
233209139Srpaulo	struct l2_packet_data *l2;
234209139Srpaulo
235209139Srpaulo	l2 = os_zalloc(sizeof(struct l2_packet_data));
236209139Srpaulo	if (l2 == NULL)
237209139Srpaulo		return NULL;
238209139Srpaulo	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
239209139Srpaulo	l2->rx_callback = rx_callback;
240209139Srpaulo	l2->rx_callback_ctx = rx_callback_ctx;
241209139Srpaulo	l2->l2_hdr = l2_hdr;
242209139Srpaulo
243209139Srpaulo	if (eth_get(l2->ifname, l2->own_addr) < 0) {
244209139Srpaulo		fprintf(stderr, "Failed to get link-level address for "
245209139Srpaulo			"interface '%s'.\n", l2->ifname);
246209139Srpaulo		os_free(l2);
247209139Srpaulo		return NULL;
248209139Srpaulo	}
249209139Srpaulo
250209139Srpaulo	if (l2_packet_init_libpcap(l2, protocol)) {
251209139Srpaulo		os_free(l2);
252209139Srpaulo		return NULL;
253209139Srpaulo	}
254209139Srpaulo
255209139Srpaulo	return l2;
256209139Srpaulo}
257209139Srpaulo
258209139Srpaulo
259281806Srpaulostruct l2_packet_data * l2_packet_init_bridge(
260281806Srpaulo	const char *br_ifname, const char *ifname, const u8 *own_addr,
261281806Srpaulo	unsigned short protocol,
262281806Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
263281806Srpaulo			    const u8 *buf, size_t len),
264281806Srpaulo	void *rx_callback_ctx, int l2_hdr)
265281806Srpaulo{
266281806Srpaulo	return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
267281806Srpaulo			      rx_callback_ctx, l2_hdr);
268281806Srpaulo}
269281806Srpaulo
270281806Srpaulo
271209139Srpaulovoid l2_packet_deinit(struct l2_packet_data *l2)
272209139Srpaulo{
273209139Srpaulo	if (l2 != NULL) {
274209139Srpaulo		if (l2->pcap) {
275209139Srpaulo			eloop_unregister_read_sock(
276209139Srpaulo				pcap_get_selectable_fd(l2->pcap));
277209139Srpaulo			pcap_close(l2->pcap);
278209139Srpaulo		}
279209139Srpaulo		os_free(l2);
280209139Srpaulo	}
281209139Srpaulo}
282209139Srpaulo
283209139Srpaulo
284209139Srpauloint l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
285209139Srpaulo{
286209139Srpaulo	pcap_if_t *devs, *dev;
287209139Srpaulo	struct pcap_addr *addr;
288209139Srpaulo	struct sockaddr_in *saddr;
289209139Srpaulo	int found = 0;
290209139Srpaulo	char err[PCAP_ERRBUF_SIZE + 1];
291209139Srpaulo
292209139Srpaulo	if (pcap_findalldevs(&devs, err) < 0) {
293209139Srpaulo		wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
294209139Srpaulo		return -1;
295209139Srpaulo	}
296209139Srpaulo
297209139Srpaulo	for (dev = devs; dev && !found; dev = dev->next) {
298209139Srpaulo		if (os_strcmp(dev->name, l2->ifname) != 0)
299209139Srpaulo			continue;
300209139Srpaulo
301209139Srpaulo		addr = dev->addresses;
302209139Srpaulo		while (addr) {
303209139Srpaulo			saddr = (struct sockaddr_in *) addr->addr;
304209139Srpaulo			if (saddr && saddr->sin_family == AF_INET) {
305209139Srpaulo				os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
306209139Srpaulo					   len);
307209139Srpaulo				found = 1;
308209139Srpaulo				break;
309209139Srpaulo			}
310209139Srpaulo			addr = addr->next;
311209139Srpaulo		}
312209139Srpaulo	}
313209139Srpaulo
314209139Srpaulo	pcap_freealldevs(devs);
315209139Srpaulo
316209139Srpaulo	return found ? 0 : -1;
317209139Srpaulo}
318209139Srpaulo
319209139Srpaulo
320209139Srpaulovoid l2_packet_notify_auth_start(struct l2_packet_data *l2)
321209139Srpaulo{
322209139Srpaulo}
323281806Srpaulo
324281806Srpaulo
325281806Srpauloint l2_packet_set_packet_filter(struct l2_packet_data *l2,
326281806Srpaulo				enum l2_packet_filter_type type)
327281806Srpaulo{
328281806Srpaulo	return -1;
329281806Srpaulo}
330