LCOV - code coverage report
Current view: top level - src/l2_packet - l2_packet_linux.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1422976643 Lines: 118 168 70.2 %
Date: 2015-02-03 Functions: 10 10 100.0 %

          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 "l2_packet.h"
      18             : 
      19             : 
      20             : struct l2_packet_data {
      21             :         int fd; /* packet socket for EAPOL frames */
      22             :         char ifname[IFNAMSIZ + 1];
      23             :         int ifindex;
      24             :         u8 own_addr[ETH_ALEN];
      25             :         void (*rx_callback)(void *ctx, const u8 *src_addr,
      26             :                             const u8 *buf, size_t len);
      27             :         void *rx_callback_ctx;
      28             :         int l2_hdr; /* whether to include layer 2 (Ethernet) header data
      29             :                      * buffers */
      30             : 
      31             :         /* For working around Linux packet socket behavior and regression. */
      32             :         int fd_br_rx;
      33             : };
      34             : 
      35             : /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
      36             :  * src port bootps and dst port bootpc'
      37             :  */
      38             : static struct sock_filter dhcp_sock_filter_insns[] = {
      39             :         { 0x80, 0, 0, 0x00000000 },
      40             :         { 0x35, 0, 12, 0x00000116 },
      41             :         { 0x28, 0, 0, 0x0000000c },
      42             :         { 0x15, 0, 10, 0x00000800 },
      43             :         { 0x30, 0, 0, 0x00000017 },
      44             :         { 0x15, 0, 8, 0x00000011 },
      45             :         { 0x28, 0, 0, 0x00000014 },
      46             :         { 0x45, 6, 0, 0x00001fff },
      47             :         { 0xb1, 0, 0, 0x0000000e },
      48             :         { 0x48, 0, 0, 0x0000000e },
      49             :         { 0x15, 0, 3, 0x00000043 },
      50             :         { 0x48, 0, 0, 0x00000010 },
      51             :         { 0x15, 0, 1, 0x00000044 },
      52             :         { 0x6, 0, 0, 0x00000bb8 },
      53             :         { 0x6, 0, 0, 0x00000000 },
      54             : };
      55             : 
      56             : static const struct sock_fprog dhcp_sock_filter = {
      57             :         .len = ARRAY_SIZE(dhcp_sock_filter_insns),
      58             :         .filter = dhcp_sock_filter_insns,
      59             : };
      60             : 
      61             : 
      62             : /* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */
      63             : static struct sock_filter ndisc_sock_filter_insns[] = {
      64             :         { 0x30, 0, 0, 0x00000000 },
      65             :         { 0x45, 0, 5, 0x00000001 },
      66             :         { 0x28, 0, 0, 0x0000000c },
      67             :         { 0x15, 0, 3, 0x000086dd },
      68             :         { 0x30, 0, 0, 0x00000014 },
      69             :         { 0x15, 0, 1, 0x0000003a },
      70             :         { 0x6, 0, 0, 0x000005dc },
      71             :         { 0x6, 0, 0, 0x00000000 },
      72             : };
      73             : 
      74             : static const struct sock_fprog ndisc_sock_filter = {
      75             :         .len = ARRAY_SIZE(ndisc_sock_filter_insns),
      76             :         .filter = ndisc_sock_filter_insns,
      77             : };
      78             : 
      79             : 
      80         211 : int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
      81             : {
      82         211 :         os_memcpy(addr, l2->own_addr, ETH_ALEN);
      83         211 :         return 0;
      84             : }
      85             : 
      86             : 
      87       11372 : int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
      88             :                    const u8 *buf, size_t len)
      89             : {
      90             :         int ret;
      91       11372 :         if (l2 == NULL)
      92           0 :                 return -1;
      93       11372 :         if (l2->l2_hdr) {
      94        3324 :                 ret = send(l2->fd, buf, len, 0);
      95        3324 :                 if (ret < 0)
      96           0 :                         wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
      97           0 :                                    strerror(errno));
      98             :         } else {
      99             :                 struct sockaddr_ll ll;
     100        8048 :                 os_memset(&ll, 0, sizeof(ll));
     101        8048 :                 ll.sll_family = AF_PACKET;
     102        8048 :                 ll.sll_ifindex = l2->ifindex;
     103        8048 :                 ll.sll_protocol = htons(proto);
     104        8048 :                 ll.sll_halen = ETH_ALEN;
     105        8048 :                 os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
     106        8048 :                 ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
     107             :                              sizeof(ll));
     108        8048 :                 if (ret < 0) {
     109           0 :                         wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
     110           0 :                                    strerror(errno));
     111             :                 }
     112             :         }
     113       11372 :         return ret;
     114             : }
     115             : 
     116             : 
     117       14164 : static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
     118             : {
     119       14164 :         struct l2_packet_data *l2 = eloop_ctx;
     120             :         u8 buf[2300];
     121             :         int res;
     122             :         struct sockaddr_ll ll;
     123             :         socklen_t fromlen;
     124             : 
     125       14164 :         os_memset(&ll, 0, sizeof(ll));
     126       14164 :         fromlen = sizeof(ll);
     127       14164 :         res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
     128             :                        &fromlen);
     129       14164 :         if (res < 0) {
     130         182 :                 wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s",
     131         182 :                            strerror(errno));
     132       14346 :                 return;
     133             :         }
     134             : 
     135       13982 :         l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
     136             : 
     137       13982 :         if (l2->fd_br_rx >= 0) {
     138           0 :                 wpa_printf(MSG_DEBUG, "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
     139           0 :                            l2->ifname);
     140           0 :                 eloop_unregister_read_sock(l2->fd_br_rx);
     141           0 :                 close(l2->fd_br_rx);
     142           0 :                 l2->fd_br_rx = -1;
     143             :         }
     144             : }
     145             : 
     146             : 
     147           4 : static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
     148             : {
     149           4 :         struct l2_packet_data *l2 = eloop_ctx;
     150             :         u8 buf[2300];
     151             :         int res;
     152             :         struct sockaddr_ll ll;
     153             :         socklen_t fromlen;
     154             : 
     155           4 :         os_memset(&ll, 0, sizeof(ll));
     156           4 :         fromlen = sizeof(ll);
     157           4 :         res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
     158             :                        &fromlen);
     159           4 :         if (res < 0) {
     160           0 :                 wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
     161           0 :                            strerror(errno));
     162           4 :                 return;
     163             :         }
     164             : 
     165           4 :         l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
     166             : }
     167             : 
     168             : 
     169        2925 : struct l2_packet_data * l2_packet_init(
     170             :         const char *ifname, const u8 *own_addr, unsigned short protocol,
     171             :         void (*rx_callback)(void *ctx, const u8 *src_addr,
     172             :                             const u8 *buf, size_t len),
     173             :         void *rx_callback_ctx, int l2_hdr)
     174             : {
     175             :         struct l2_packet_data *l2;
     176             :         struct ifreq ifr;
     177             :         struct sockaddr_ll ll;
     178             : 
     179        2925 :         l2 = os_zalloc(sizeof(struct l2_packet_data));
     180        2925 :         if (l2 == NULL)
     181           3 :                 return NULL;
     182        2922 :         os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
     183        2922 :         l2->rx_callback = rx_callback;
     184        2922 :         l2->rx_callback_ctx = rx_callback_ctx;
     185        2922 :         l2->l2_hdr = l2_hdr;
     186        2922 :         l2->fd_br_rx = -1;
     187             : 
     188        2922 :         l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
     189        2922 :                         htons(protocol));
     190        2922 :         if (l2->fd < 0) {
     191           0 :                 wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s",
     192           0 :                            __func__, strerror(errno));
     193           0 :                 os_free(l2);
     194           0 :                 return NULL;
     195             :         }
     196        2922 :         os_memset(&ifr, 0, sizeof(ifr));
     197        2922 :         os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
     198        2922 :         if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
     199           1 :                 wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s",
     200           1 :                            __func__, strerror(errno));
     201           1 :                 close(l2->fd);
     202           1 :                 os_free(l2);
     203           1 :                 return NULL;
     204             :         }
     205        2921 :         l2->ifindex = ifr.ifr_ifindex;
     206             : 
     207        2921 :         os_memset(&ll, 0, sizeof(ll));
     208        2921 :         ll.sll_family = PF_PACKET;
     209        2921 :         ll.sll_ifindex = ifr.ifr_ifindex;
     210        2921 :         ll.sll_protocol = htons(protocol);
     211        2921 :         if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
     212           0 :                 wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s",
     213           0 :                            __func__, strerror(errno));
     214           0 :                 close(l2->fd);
     215           0 :                 os_free(l2);
     216           0 :                 return NULL;
     217             :         }
     218             : 
     219        2921 :         if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
     220           0 :                 wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s",
     221           0 :                            __func__, strerror(errno));
     222           0 :                 close(l2->fd);
     223           0 :                 os_free(l2);
     224           0 :                 return NULL;
     225             :         }
     226        2921 :         os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
     227             : 
     228        2921 :         eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
     229             : 
     230        2921 :         return l2;
     231             : }
     232             : 
     233             : 
     234           3 : struct l2_packet_data * l2_packet_init_bridge(
     235             :         const char *br_ifname, const char *ifname, const u8 *own_addr,
     236             :         unsigned short protocol,
     237             :         void (*rx_callback)(void *ctx, const u8 *src_addr,
     238             :                             const u8 *buf, size_t len),
     239             :         void *rx_callback_ctx, int l2_hdr)
     240             : {
     241             :         struct l2_packet_data *l2;
     242           3 :         struct sock_filter ethertype_sock_filter_insns[] = {
     243             :                 /* Load ethertype */
     244             :                 BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
     245             :                 /* Jump over next statement if ethertype does not match */
     246             :                 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
     247             :                 /* Ethertype match - return all */
     248             :                 BPF_STMT(BPF_RET | BPF_K, ~0),
     249             :                 /* No match - drop */
     250             :                 BPF_STMT(BPF_RET | BPF_K, 0)
     251             :         };
     252           3 :         const struct sock_fprog ethertype_sock_filter = {
     253             :                 .len = ARRAY_SIZE(ethertype_sock_filter_insns),
     254             :                 .filter = ethertype_sock_filter_insns,
     255             :         };
     256             :         struct sockaddr_ll ll;
     257             : 
     258           3 :         l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
     259             :                             rx_callback_ctx, l2_hdr);
     260           3 :         if (!l2)
     261           1 :                 return NULL;
     262             : 
     263             :         /*
     264             :          * The Linux packet socket behavior has changed over the years and there
     265             :          * is an inconvenient regression in it that breaks RX for a specific
     266             :          * protocol from interfaces in a bridge when that interface is not in
     267             :          * fully operation state (i.e., when in station mode and not completed
     268             :          * authorization). To work around this, register ETH_P_ALL version of
     269             :          * the packet socket bound to the real netdev and use socket filter to
     270             :          * match the ethertype value. This version is less efficient, but
     271             :          * required for functionality with many kernel version. If the main
     272             :          * packet socket is found to be working, this less efficient version
     273             :          * gets closed automatically.
     274             :          */
     275             : 
     276           2 :         l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
     277           2 :                               htons(ETH_P_ALL));
     278           2 :         if (l2->fd_br_rx < 0) {
     279           0 :                 wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
     280           0 :                            __func__, strerror(errno));
     281             :                 /* try to continue without the workaround RX socket */
     282           0 :                 return l2;
     283             :         }
     284             : 
     285           2 :         os_memset(&ll, 0, sizeof(ll));
     286           2 :         ll.sll_family = PF_PACKET;
     287           2 :         ll.sll_ifindex = if_nametoindex(ifname);
     288           2 :         ll.sll_protocol = htons(ETH_P_ALL);
     289           2 :         if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
     290           0 :                 wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
     291           0 :                            __func__, strerror(errno));
     292             :                 /* try to continue without the workaround RX socket */
     293           0 :                 close(l2->fd_br_rx);
     294           0 :                 l2->fd_br_rx = -1;
     295           0 :                 return l2;
     296             :         }
     297             : 
     298           2 :         if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
     299             :                        &ethertype_sock_filter, sizeof(struct sock_fprog))) {
     300           0 :                 wpa_printf(MSG_DEBUG,
     301             :                            "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
     302           0 :                            strerror(errno));
     303             :                 /* try to continue without the workaround RX socket */
     304           0 :                 close(l2->fd_br_rx);
     305           0 :                 l2->fd_br_rx = -1;
     306           0 :                 return l2;
     307             :         }
     308             : 
     309           2 :         eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
     310             : 
     311           2 :         return l2;
     312             : }
     313             : 
     314             : 
     315        6788 : void l2_packet_deinit(struct l2_packet_data *l2)
     316             : {
     317        6788 :         if (l2 == NULL)
     318       10655 :                 return;
     319             : 
     320        2921 :         if (l2->fd >= 0) {
     321        2921 :                 eloop_unregister_read_sock(l2->fd);
     322        2921 :                 close(l2->fd);
     323             :         }
     324             : 
     325        2921 :         if (l2->fd_br_rx >= 0) {
     326           2 :                 eloop_unregister_read_sock(l2->fd_br_rx);
     327           2 :                 close(l2->fd_br_rx);
     328             :         }
     329             : 
     330        2921 :         os_free(l2);
     331             : }
     332             : 
     333             : 
     334        2517 : int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
     335             : {
     336             :         int s;
     337             :         struct ifreq ifr;
     338             :         struct sockaddr_in *saddr;
     339             :         size_t res;
     340             : 
     341        2517 :         s = socket(PF_INET, SOCK_DGRAM, 0);
     342        2517 :         if (s < 0) {
     343           0 :                 wpa_printf(MSG_ERROR, "%s: socket: %s",
     344           0 :                            __func__, strerror(errno));
     345           0 :                 return -1;
     346             :         }
     347        2517 :         os_memset(&ifr, 0, sizeof(ifr));
     348        2517 :         os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
     349        2517 :         if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
     350        2516 :                 if (errno != EADDRNOTAVAIL)
     351           0 :                         wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s",
     352           0 :                                    __func__, strerror(errno));
     353        2516 :                 close(s);
     354        2516 :                 return -1;
     355             :         }
     356           1 :         close(s);
     357           1 :         saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in);
     358           1 :         if (saddr->sin_family != AF_INET)
     359           0 :                 return -1;
     360           1 :         res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len);
     361           1 :         if (res >= len)
     362           0 :                 return -1;
     363           1 :         return 0;
     364             : }
     365             : 
     366             : 
     367        2472 : void l2_packet_notify_auth_start(struct l2_packet_data *l2)
     368             : {
     369        2472 : }
     370             : 
     371             : 
     372          10 : int l2_packet_set_packet_filter(struct l2_packet_data *l2,
     373             :                                 enum l2_packet_filter_type type)
     374             : {
     375             :         const struct sock_fprog *sock_filter;
     376             : 
     377          10 :         switch (type) {
     378             :         case L2_PACKET_FILTER_DHCP:
     379           5 :                 sock_filter = &dhcp_sock_filter;
     380           5 :                 break;
     381             :         case L2_PACKET_FILTER_NDISC:
     382           5 :                 sock_filter = &ndisc_sock_filter;
     383           5 :                 break;
     384             :         default:
     385           0 :                 return -1;
     386             :         }
     387             : 
     388          10 :         if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER,
     389             :                        sock_filter, sizeof(struct sock_fprog))) {
     390           0 :                 wpa_printf(MSG_ERROR,
     391             :                            "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
     392           0 :                            strerror(errno));
     393           0 :                 return -1;
     394             :         }
     395             : 
     396          10 :         return 0;
     397             : }

Generated by: LCOV version 1.10