Line data Source code
1 : /*
2 : * WPA Supplicant - Layer2 packet handling with Linux packet sockets
3 : * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
4 : *
5 : * This software may be distributed under the terms of the BSD license.
6 : * See README for more details.
7 : */
8 :
9 : #include "includes.h"
10 : #include <sys/ioctl.h>
11 : #include <netpacket/packet.h>
12 : #include <net/if.h>
13 : #include <linux/filter.h>
14 :
15 : #include "common.h"
16 : #include "eloop.h"
17 : #include "crypto/sha1.h"
18 : #include "crypto/crypto.h"
19 : #include "l2_packet.h"
20 :
21 :
22 : struct l2_packet_data {
23 : int fd; /* packet socket for EAPOL frames */
24 : char ifname[IFNAMSIZ + 1];
25 : int ifindex;
26 : u8 own_addr[ETH_ALEN];
27 : void (*rx_callback)(void *ctx, const u8 *src_addr,
28 : const u8 *buf, size_t len);
29 : void *rx_callback_ctx;
30 : int l2_hdr; /* whether to include layer 2 (Ethernet) header data
31 : * buffers */
32 :
33 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
34 : /* For working around Linux packet socket behavior and regression. */
35 : int fd_br_rx;
36 : int last_from_br, last_from_br_prev;
37 : u8 last_hash[SHA1_MAC_LEN];
38 : u8 last_hash_prev[SHA1_MAC_LEN];
39 : unsigned int num_rx_br;
40 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
41 : };
42 :
43 : /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
44 : * src port bootps and dst port bootpc'
45 : */
46 : static struct sock_filter dhcp_sock_filter_insns[] = {
47 : { 0x80, 0, 0, 0x00000000 },
48 : { 0x35, 0, 12, 0x00000116 },
49 : { 0x28, 0, 0, 0x0000000c },
50 : { 0x15, 0, 10, 0x00000800 },
51 : { 0x30, 0, 0, 0x00000017 },
52 : { 0x15, 0, 8, 0x00000011 },
53 : { 0x28, 0, 0, 0x00000014 },
54 : { 0x45, 6, 0, 0x00001fff },
55 : { 0xb1, 0, 0, 0x0000000e },
56 : { 0x48, 0, 0, 0x0000000e },
57 : { 0x15, 0, 3, 0x00000043 },
58 : { 0x48, 0, 0, 0x00000010 },
59 : { 0x15, 0, 1, 0x00000044 },
60 : { 0x6, 0, 0, 0x00000bb8 },
61 : { 0x6, 0, 0, 0x00000000 },
62 : };
63 :
64 : static const struct sock_fprog dhcp_sock_filter = {
65 : .len = ARRAY_SIZE(dhcp_sock_filter_insns),
66 : .filter = dhcp_sock_filter_insns,
67 : };
68 :
69 :
70 : /* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */
71 : static struct sock_filter ndisc_sock_filter_insns[] = {
72 : { 0x30, 0, 0, 0x00000000 },
73 : { 0x45, 0, 5, 0x00000001 },
74 : { 0x28, 0, 0, 0x0000000c },
75 : { 0x15, 0, 3, 0x000086dd },
76 : { 0x30, 0, 0, 0x00000014 },
77 : { 0x15, 0, 1, 0x0000003a },
78 : { 0x6, 0, 0, 0x000005dc },
79 : { 0x6, 0, 0, 0x00000000 },
80 : };
81 :
82 : static const struct sock_fprog ndisc_sock_filter = {
83 : .len = ARRAY_SIZE(ndisc_sock_filter_insns),
84 : .filter = ndisc_sock_filter_insns,
85 : };
86 :
87 :
88 714 : int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
89 : {
90 714 : os_memcpy(addr, l2->own_addr, ETH_ALEN);
91 714 : return 0;
92 : }
93 :
94 :
95 21066 : int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
96 : const u8 *buf, size_t len)
97 : {
98 : int ret;
99 21066 : if (l2 == NULL)
100 0 : return -1;
101 21066 : if (l2->l2_hdr) {
102 4787 : ret = send(l2->fd, buf, len, 0);
103 4787 : if (ret < 0)
104 0 : wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
105 0 : strerror(errno));
106 : } else {
107 : struct sockaddr_ll ll;
108 16279 : os_memset(&ll, 0, sizeof(ll));
109 16279 : ll.sll_family = AF_PACKET;
110 16279 : ll.sll_ifindex = l2->ifindex;
111 16279 : ll.sll_protocol = htons(proto);
112 16279 : ll.sll_halen = ETH_ALEN;
113 16279 : os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
114 16279 : ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
115 : sizeof(ll));
116 16279 : if (ret < 0) {
117 0 : wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
118 0 : strerror(errno));
119 : }
120 : }
121 21066 : return ret;
122 : }
123 :
124 :
125 26506 : static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
126 : {
127 26506 : struct l2_packet_data *l2 = eloop_ctx;
128 : u8 buf[2300];
129 : int res;
130 : struct sockaddr_ll ll;
131 : socklen_t fromlen;
132 :
133 26506 : os_memset(&ll, 0, sizeof(ll));
134 26506 : fromlen = sizeof(ll);
135 26506 : res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
136 : &fromlen);
137 26506 : if (res < 0) {
138 809 : wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s",
139 809 : strerror(errno));
140 809 : return;
141 : }
142 :
143 154182 : wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
144 154182 : __func__, MAC2STR(ll.sll_addr), (int) res);
145 :
146 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
147 25697 : if (l2->fd_br_rx >= 0) {
148 : u8 hash[SHA1_MAC_LEN];
149 : const u8 *addr[1];
150 : size_t len[1];
151 :
152 : /*
153 : * Close the workaround socket if the kernel version seems to be
154 : * able to deliver packets through the packet socket before
155 : * authorization has been completed (in dormant state).
156 : */
157 12 : if (l2->num_rx_br <= 1) {
158 0 : wpa_printf(MSG_DEBUG,
159 : "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
160 0 : l2->ifname);
161 0 : eloop_unregister_read_sock(l2->fd_br_rx);
162 0 : close(l2->fd_br_rx);
163 0 : l2->fd_br_rx = -1;
164 : }
165 :
166 12 : addr[0] = buf;
167 12 : len[0] = res;
168 12 : sha1_vector(1, addr, len, hash);
169 17 : if (l2->last_from_br &&
170 5 : os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
171 3 : wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX",
172 : __func__);
173 3 : return;
174 : }
175 13 : if (l2->last_from_br_prev &&
176 4 : os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) {
177 0 : wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)",
178 : __func__);
179 0 : return;
180 : }
181 9 : os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN);
182 9 : l2->last_from_br_prev = l2->last_from_br;
183 9 : os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
184 : }
185 :
186 25694 : l2->last_from_br = 0;
187 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
188 25694 : l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
189 : }
190 :
191 :
192 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
193 43 : static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
194 : {
195 43 : struct l2_packet_data *l2 = eloop_ctx;
196 : u8 buf[2300];
197 : int res;
198 : struct sockaddr_ll ll;
199 : socklen_t fromlen;
200 : u8 hash[SHA1_MAC_LEN];
201 : const u8 *addr[1];
202 : size_t len[1];
203 :
204 43 : l2->num_rx_br++;
205 43 : os_memset(&ll, 0, sizeof(ll));
206 43 : fromlen = sizeof(ll);
207 43 : res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
208 : &fromlen);
209 43 : if (res < 0) {
210 0 : wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
211 0 : strerror(errno));
212 0 : return;
213 : }
214 :
215 258 : wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
216 258 : __func__, MAC2STR(ll.sll_addr), (int) res);
217 :
218 43 : if (os_memcmp(ll.sll_addr, l2->own_addr, ETH_ALEN) == 0) {
219 21 : wpa_printf(MSG_DEBUG, "%s: Drop RX of own frame", __func__);
220 21 : return;
221 : }
222 :
223 22 : addr[0] = buf;
224 22 : len[0] = res;
225 22 : sha1_vector(1, addr, len, hash);
226 35 : if (!l2->last_from_br &&
227 13 : os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
228 9 : wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
229 9 : return;
230 : }
231 21 : if (!l2->last_from_br_prev &&
232 8 : os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) {
233 0 : wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)", __func__);
234 0 : return;
235 : }
236 13 : os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN);
237 13 : l2->last_from_br_prev = l2->last_from_br;
238 13 : l2->last_from_br = 1;
239 13 : os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
240 13 : l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
241 : }
242 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
243 :
244 :
245 3882 : struct l2_packet_data * l2_packet_init(
246 : const char *ifname, const u8 *own_addr, unsigned short protocol,
247 : void (*rx_callback)(void *ctx, const u8 *src_addr,
248 : const u8 *buf, size_t len),
249 : void *rx_callback_ctx, int l2_hdr)
250 : {
251 : struct l2_packet_data *l2;
252 : struct ifreq ifr;
253 : struct sockaddr_ll ll;
254 :
255 3882 : l2 = os_zalloc(sizeof(struct l2_packet_data));
256 3882 : if (l2 == NULL)
257 4 : return NULL;
258 3878 : os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
259 3878 : l2->rx_callback = rx_callback;
260 3878 : l2->rx_callback_ctx = rx_callback_ctx;
261 3878 : l2->l2_hdr = l2_hdr;
262 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
263 3878 : l2->fd_br_rx = -1;
264 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
265 :
266 3878 : l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
267 3878 : htons(protocol));
268 3878 : if (l2->fd < 0) {
269 0 : wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s",
270 0 : __func__, strerror(errno));
271 0 : os_free(l2);
272 0 : return NULL;
273 : }
274 3878 : os_memset(&ifr, 0, sizeof(ifr));
275 3878 : os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
276 3878 : if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
277 1 : wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s",
278 1 : __func__, strerror(errno));
279 1 : close(l2->fd);
280 1 : os_free(l2);
281 1 : return NULL;
282 : }
283 3877 : l2->ifindex = ifr.ifr_ifindex;
284 :
285 3877 : os_memset(&ll, 0, sizeof(ll));
286 3877 : ll.sll_family = PF_PACKET;
287 3877 : ll.sll_ifindex = ifr.ifr_ifindex;
288 3877 : ll.sll_protocol = htons(protocol);
289 3877 : if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
290 0 : wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s",
291 0 : __func__, strerror(errno));
292 0 : close(l2->fd);
293 0 : os_free(l2);
294 0 : return NULL;
295 : }
296 :
297 3877 : if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
298 0 : wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s",
299 0 : __func__, strerror(errno));
300 0 : close(l2->fd);
301 0 : os_free(l2);
302 0 : return NULL;
303 : }
304 3877 : os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
305 :
306 3877 : eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
307 :
308 3877 : return l2;
309 : }
310 :
311 :
312 4 : struct l2_packet_data * l2_packet_init_bridge(
313 : const char *br_ifname, const char *ifname, const u8 *own_addr,
314 : unsigned short protocol,
315 : void (*rx_callback)(void *ctx, const u8 *src_addr,
316 : const u8 *buf, size_t len),
317 : void *rx_callback_ctx, int l2_hdr)
318 : {
319 : struct l2_packet_data *l2;
320 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
321 4 : struct sock_filter ethertype_sock_filter_insns[] = {
322 : /* Load ethertype */
323 : BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
324 : /* Jump over next statement if ethertype does not match */
325 : BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
326 : /* Ethertype match - return all */
327 : BPF_STMT(BPF_RET | BPF_K, ~0),
328 : /* No match - drop */
329 : BPF_STMT(BPF_RET | BPF_K, 0)
330 : };
331 4 : const struct sock_fprog ethertype_sock_filter = {
332 : .len = ARRAY_SIZE(ethertype_sock_filter_insns),
333 : .filter = ethertype_sock_filter_insns,
334 : };
335 : struct sockaddr_ll ll;
336 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
337 :
338 4 : l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
339 : rx_callback_ctx, l2_hdr);
340 4 : if (!l2)
341 1 : return NULL;
342 :
343 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
344 : /*
345 : * The Linux packet socket behavior has changed over the years and there
346 : * is an inconvenient regression in it that breaks RX for a specific
347 : * protocol from interfaces in a bridge when that interface is not in
348 : * fully operation state (i.e., when in station mode and not completed
349 : * authorization). To work around this, register ETH_P_ALL version of
350 : * the packet socket bound to the real netdev and use socket filter to
351 : * match the ethertype value. This version is less efficient, but
352 : * required for functionality with many kernel version. If the main
353 : * packet socket is found to be working, this less efficient version
354 : * gets closed automatically.
355 : */
356 :
357 3 : l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
358 3 : htons(ETH_P_ALL));
359 3 : if (l2->fd_br_rx < 0) {
360 0 : wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
361 0 : __func__, strerror(errno));
362 : /* try to continue without the workaround RX socket */
363 0 : return l2;
364 : }
365 :
366 3 : os_memset(&ll, 0, sizeof(ll));
367 3 : ll.sll_family = PF_PACKET;
368 3 : ll.sll_ifindex = if_nametoindex(ifname);
369 3 : ll.sll_protocol = htons(ETH_P_ALL);
370 3 : if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
371 0 : wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
372 0 : __func__, strerror(errno));
373 : /* try to continue without the workaround RX socket */
374 0 : close(l2->fd_br_rx);
375 0 : l2->fd_br_rx = -1;
376 0 : return l2;
377 : }
378 :
379 3 : if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
380 : ðertype_sock_filter, sizeof(struct sock_fprog))) {
381 0 : wpa_printf(MSG_DEBUG,
382 : "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
383 0 : strerror(errno));
384 : /* try to continue without the workaround RX socket */
385 0 : close(l2->fd_br_rx);
386 0 : l2->fd_br_rx = -1;
387 0 : return l2;
388 : }
389 :
390 3 : eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
391 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
392 :
393 3 : return l2;
394 : }
395 :
396 :
397 14158 : void l2_packet_deinit(struct l2_packet_data *l2)
398 : {
399 14158 : if (l2 == NULL)
400 24439 : return;
401 :
402 3877 : if (l2->fd >= 0) {
403 3877 : eloop_unregister_read_sock(l2->fd);
404 3877 : close(l2->fd);
405 : }
406 :
407 : #ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
408 3877 : if (l2->fd_br_rx >= 0) {
409 3 : eloop_unregister_read_sock(l2->fd_br_rx);
410 3 : close(l2->fd_br_rx);
411 : }
412 : #endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
413 :
414 3877 : os_free(l2);
415 : }
416 :
417 :
418 4570 : int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
419 : {
420 : int s;
421 : struct ifreq ifr;
422 : struct sockaddr_in *saddr;
423 : size_t res;
424 :
425 4570 : s = socket(PF_INET, SOCK_DGRAM, 0);
426 4570 : if (s < 0) {
427 0 : wpa_printf(MSG_ERROR, "%s: socket: %s",
428 0 : __func__, strerror(errno));
429 0 : return -1;
430 : }
431 4570 : os_memset(&ifr, 0, sizeof(ifr));
432 4570 : os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
433 4570 : if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
434 4568 : if (errno != EADDRNOTAVAIL)
435 0 : wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s",
436 0 : __func__, strerror(errno));
437 4568 : close(s);
438 4568 : return -1;
439 : }
440 2 : close(s);
441 2 : saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in);
442 2 : if (saddr->sin_family != AF_INET)
443 0 : return -1;
444 2 : res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len);
445 2 : if (res >= len)
446 0 : return -1;
447 2 : return 0;
448 : }
449 :
450 :
451 4777 : void l2_packet_notify_auth_start(struct l2_packet_data *l2)
452 : {
453 4777 : }
454 :
455 :
456 24 : int l2_packet_set_packet_filter(struct l2_packet_data *l2,
457 : enum l2_packet_filter_type type)
458 : {
459 : const struct sock_fprog *sock_filter;
460 :
461 24 : switch (type) {
462 : case L2_PACKET_FILTER_DHCP:
463 12 : sock_filter = &dhcp_sock_filter;
464 12 : break;
465 : case L2_PACKET_FILTER_NDISC:
466 12 : sock_filter = &ndisc_sock_filter;
467 12 : break;
468 : default:
469 0 : return -1;
470 : }
471 :
472 24 : if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER,
473 : sock_filter, sizeof(struct sock_fprog))) {
474 0 : wpa_printf(MSG_ERROR,
475 : "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
476 0 : strerror(errno));
477 0 : return -1;
478 : }
479 :
480 24 : return 0;
481 : }
|