l2_packet_freebsd.c revision 209139
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 *
6209139Srpaulo * This program is free software; you can redistribute it and/or modify
7209139Srpaulo * it under the terms of the GNU General Public License version 2 as
8209139Srpaulo * published by the Free Software Foundation.
9209139Srpaulo *
10209139Srpaulo * Alternatively, this software may be distributed under the terms of BSD
11209139Srpaulo * license.
12209139Srpaulo *
13209139Srpaulo * See README and COPYING for more details.
14209139Srpaulo */
15209139Srpaulo
16209139Srpaulo#include "includes.h"
17209139Srpaulo#ifdef __APPLE__
18209139Srpaulo#include <net/bpf.h>
19209139Srpaulo#endif /* __APPLE__ */
20209139Srpaulo#include <pcap.h>
21209139Srpaulo
22209139Srpaulo#include <sys/ioctl.h>
23209139Srpaulo#include <sys/sysctl.h>
24209139Srpaulo
25209139Srpaulo#include <net/if.h>
26209139Srpaulo#include <net/if_dl.h>
27209139Srpaulo#include <net/route.h>
28209139Srpaulo#include <netinet/in.h>
29209139Srpaulo
30209139Srpaulo#include "common.h"
31209139Srpaulo#include "eloop.h"
32209139Srpaulo#include "l2_packet.h"
33209139Srpaulo
34209139Srpaulo
35209139Srpaulostatic const u8 pae_group_addr[ETH_ALEN] =
36209139Srpaulo{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
37209139Srpaulo
38209139Srpaulostruct l2_packet_data {
39209139Srpaulo	pcap_t *pcap;
40209139Srpaulo	char ifname[100];
41209139Srpaulo	u8 own_addr[ETH_ALEN];
42209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
43209139Srpaulo			    const u8 *buf, size_t len);
44209139Srpaulo	void *rx_callback_ctx;
45209139Srpaulo	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
46209139Srpaulo		     * buffers */
47209139Srpaulo};
48209139Srpaulo
49209139Srpaulo
50209139Srpauloint l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
51209139Srpaulo{
52209139Srpaulo	os_memcpy(addr, l2->own_addr, ETH_ALEN);
53209139Srpaulo	return 0;
54209139Srpaulo}
55209139Srpaulo
56209139Srpaulo
57209139Srpauloint l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
58209139Srpaulo		   const u8 *buf, size_t len)
59209139Srpaulo{
60209139Srpaulo	if (!l2->l2_hdr) {
61209139Srpaulo		int ret;
62209139Srpaulo		struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
63209139Srpaulo		if (eth == NULL)
64209139Srpaulo			return -1;
65209139Srpaulo		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
66209139Srpaulo		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
67209139Srpaulo		eth->h_proto = htons(proto);
68209139Srpaulo		os_memcpy(eth + 1, buf, len);
69209139Srpaulo		ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
70209139Srpaulo		os_free(eth);
71209139Srpaulo		return ret;
72209139Srpaulo	} else
73209139Srpaulo		return pcap_inject(l2->pcap, buf, len);
74209139Srpaulo}
75209139Srpaulo
76209139Srpaulo
77209139Srpaulostatic void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
78209139Srpaulo{
79209139Srpaulo	struct l2_packet_data *l2 = eloop_ctx;
80209139Srpaulo	pcap_t *pcap = sock_ctx;
81209139Srpaulo	struct pcap_pkthdr hdr;
82209139Srpaulo	const u_char *packet;
83209139Srpaulo	struct l2_ethhdr *ethhdr;
84209139Srpaulo	unsigned char *buf;
85209139Srpaulo	size_t len;
86209139Srpaulo
87209139Srpaulo	packet = pcap_next(pcap, &hdr);
88209139Srpaulo
89209139Srpaulo	if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
90209139Srpaulo		return;
91209139Srpaulo
92209139Srpaulo	ethhdr = (struct l2_ethhdr *) packet;
93209139Srpaulo	if (l2->l2_hdr) {
94209139Srpaulo		buf = (unsigned char *) ethhdr;
95209139Srpaulo		len = hdr.caplen;
96209139Srpaulo	} else {
97209139Srpaulo		buf = (unsigned char *) (ethhdr + 1);
98209139Srpaulo		len = hdr.caplen - sizeof(*ethhdr);
99209139Srpaulo	}
100209139Srpaulo	l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
101209139Srpaulo}
102209139Srpaulo
103209139Srpaulo
104209139Srpaulostatic int l2_packet_init_libpcap(struct l2_packet_data *l2,
105209139Srpaulo				  unsigned short protocol)
106209139Srpaulo{
107209139Srpaulo	bpf_u_int32 pcap_maskp, pcap_netp;
108209139Srpaulo	char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
109209139Srpaulo	struct bpf_program pcap_fp;
110209139Srpaulo
111209139Srpaulo	pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
112209139Srpaulo	l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
113209139Srpaulo	if (l2->pcap == NULL) {
114209139Srpaulo		fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
115209139Srpaulo		fprintf(stderr, "ifname='%s'\n", l2->ifname);
116209139Srpaulo		return -1;
117209139Srpaulo	}
118209139Srpaulo	if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
119209139Srpaulo	    pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
120209139Srpaulo		fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
121209139Srpaulo			pcap_geterr(l2->pcap));
122209139Srpaulo		return -1;
123209139Srpaulo	}
124209139Srpaulo	os_snprintf(pcap_filter, sizeof(pcap_filter),
125209139Srpaulo		    "not ether src " MACSTR " and "
126209139Srpaulo		    "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
127209139Srpaulo		    "ether proto 0x%x",
128209139Srpaulo		    MAC2STR(l2->own_addr), /* do not receive own packets */
129209139Srpaulo		    MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
130209139Srpaulo		    protocol);
131209139Srpaulo	if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
132209139Srpaulo		fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
133209139Srpaulo		return -1;
134209139Srpaulo	}
135209139Srpaulo
136209139Srpaulo	if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
137209139Srpaulo		fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
138209139Srpaulo		return -1;
139209139Srpaulo	}
140209139Srpaulo
141209139Srpaulo	pcap_freecode(&pcap_fp);
142209139Srpaulo	/*
143209139Srpaulo	 * When libpcap uses BPF we must enable "immediate mode" to
144209139Srpaulo	 * receive frames right away; otherwise the system may
145209139Srpaulo	 * buffer them for us.
146209139Srpaulo	 */
147209139Srpaulo	{
148209139Srpaulo		unsigned int on = 1;
149209139Srpaulo		if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
150209139Srpaulo			fprintf(stderr, "%s: cannot enable immediate mode on "
151209139Srpaulo				"interface %s: %s\n",
152209139Srpaulo				__func__, l2->ifname, strerror(errno));
153209139Srpaulo			/* XXX should we fail? */
154209139Srpaulo		}
155209139Srpaulo	}
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{
166209139Srpaulo	struct if_msghdr *ifm;
167209139Srpaulo	struct sockaddr_dl *sdl;
168209139Srpaulo	u_char *p, *buf;
169209139Srpaulo	size_t len;
170209139Srpaulo	int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
171209139Srpaulo
172209139Srpaulo	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
173209139Srpaulo		return -1;
174209139Srpaulo	if ((buf = os_malloc(len)) == NULL)
175209139Srpaulo		return -1;
176209139Srpaulo	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
177209139Srpaulo		os_free(buf);
178209139Srpaulo		return -1;
179209139Srpaulo	}
180209139Srpaulo	for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
181209139Srpaulo		ifm = (struct if_msghdr *)p;
182209139Srpaulo		sdl = (struct sockaddr_dl *)(ifm + 1);
183209139Srpaulo		if (ifm->ifm_type != RTM_IFINFO ||
184209139Srpaulo		    (ifm->ifm_addrs & RTA_IFP) == 0)
185209139Srpaulo			continue;
186209139Srpaulo		if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
187209139Srpaulo		    os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
188209139Srpaulo			continue;
189209139Srpaulo		os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
190209139Srpaulo		break;
191209139Srpaulo	}
192209139Srpaulo	os_free(buf);
193209139Srpaulo
194209139Srpaulo	if (p >= buf + len) {
195209139Srpaulo		errno = ESRCH;
196209139Srpaulo		return -1;
197209139Srpaulo	}
198209139Srpaulo	return 0;
199209139Srpaulo}
200209139Srpaulo
201209139Srpaulo
202209139Srpaulostruct l2_packet_data * l2_packet_init(
203209139Srpaulo	const char *ifname, const u8 *own_addr, unsigned short protocol,
204209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
205209139Srpaulo			    const u8 *buf, size_t len),
206209139Srpaulo	void *rx_callback_ctx, int l2_hdr)
207209139Srpaulo{
208209139Srpaulo	struct l2_packet_data *l2;
209209139Srpaulo
210209139Srpaulo	l2 = os_zalloc(sizeof(struct l2_packet_data));
211209139Srpaulo	if (l2 == NULL)
212209139Srpaulo		return NULL;
213209139Srpaulo	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
214209139Srpaulo	l2->rx_callback = rx_callback;
215209139Srpaulo	l2->rx_callback_ctx = rx_callback_ctx;
216209139Srpaulo	l2->l2_hdr = l2_hdr;
217209139Srpaulo
218209139Srpaulo	if (eth_get(l2->ifname, l2->own_addr) < 0) {
219209139Srpaulo		fprintf(stderr, "Failed to get link-level address for "
220209139Srpaulo			"interface '%s'.\n", l2->ifname);
221209139Srpaulo		os_free(l2);
222209139Srpaulo		return NULL;
223209139Srpaulo	}
224209139Srpaulo
225209139Srpaulo	if (l2_packet_init_libpcap(l2, protocol)) {
226209139Srpaulo		os_free(l2);
227209139Srpaulo		return NULL;
228209139Srpaulo	}
229209139Srpaulo
230209139Srpaulo	return l2;
231209139Srpaulo}
232209139Srpaulo
233209139Srpaulo
234209139Srpaulovoid l2_packet_deinit(struct l2_packet_data *l2)
235209139Srpaulo{
236209139Srpaulo	if (l2 != NULL) {
237209139Srpaulo		if (l2->pcap) {
238209139Srpaulo			eloop_unregister_read_sock(
239209139Srpaulo				pcap_get_selectable_fd(l2->pcap));
240209139Srpaulo			pcap_close(l2->pcap);
241209139Srpaulo		}
242209139Srpaulo		os_free(l2);
243209139Srpaulo	}
244209139Srpaulo}
245209139Srpaulo
246209139Srpaulo
247209139Srpauloint l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
248209139Srpaulo{
249209139Srpaulo	pcap_if_t *devs, *dev;
250209139Srpaulo	struct pcap_addr *addr;
251209139Srpaulo	struct sockaddr_in *saddr;
252209139Srpaulo	int found = 0;
253209139Srpaulo	char err[PCAP_ERRBUF_SIZE + 1];
254209139Srpaulo
255209139Srpaulo	if (pcap_findalldevs(&devs, err) < 0) {
256209139Srpaulo		wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
257209139Srpaulo		return -1;
258209139Srpaulo	}
259209139Srpaulo
260209139Srpaulo	for (dev = devs; dev && !found; dev = dev->next) {
261209139Srpaulo		if (os_strcmp(dev->name, l2->ifname) != 0)
262209139Srpaulo			continue;
263209139Srpaulo
264209139Srpaulo		addr = dev->addresses;
265209139Srpaulo		while (addr) {
266209139Srpaulo			saddr = (struct sockaddr_in *) addr->addr;
267209139Srpaulo			if (saddr && saddr->sin_family == AF_INET) {
268209139Srpaulo				os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
269209139Srpaulo					   len);
270209139Srpaulo				found = 1;
271209139Srpaulo				break;
272209139Srpaulo			}
273209139Srpaulo			addr = addr->next;
274209139Srpaulo		}
275209139Srpaulo	}
276209139Srpaulo
277209139Srpaulo	pcap_freealldevs(devs);
278209139Srpaulo
279209139Srpaulo	return found ? 0 : -1;
280209139Srpaulo}
281209139Srpaulo
282209139Srpaulo
283209139Srpaulovoid l2_packet_notify_auth_start(struct l2_packet_data *l2)
284209139Srpaulo{
285209139Srpaulo}
286