LCOV - code coverage report
Current view: top level - src/ap - ndisc_snoop.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 67 76 88.2 %
Date: 2015-09-27 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Neighbor Discovery snooping for Proxy ARP
       3             :  * Copyright (c) 2014, Qualcomm Atheros, Inc.
       4             :  *
       5             :  * This software may be distributed under the terms of the BSD license.
       6             :  * See README for more details.
       7             :  */
       8             : 
       9             : #include "utils/includes.h"
      10             : #include <netinet/ip6.h>
      11             : #include <netinet/icmp6.h>
      12             : 
      13             : #include "utils/common.h"
      14             : #include "l2_packet/l2_packet.h"
      15             : #include "hostapd.h"
      16             : #include "sta_info.h"
      17             : #include "ap_drv_ops.h"
      18             : #include "list.h"
      19             : #include "x_snoop.h"
      20             : 
      21             : struct ip6addr {
      22             :         struct in6_addr addr;
      23             :         struct dl_list list;
      24             : };
      25             : 
      26             : struct icmpv6_ndmsg {
      27             :         struct ip6_hdr ipv6h;
      28             :         struct icmp6_hdr icmp6h;
      29             :         struct in6_addr target_addr;
      30             :         u8 opt_type;
      31             :         u8 len;
      32             :         u8 opt_lladdr[0];
      33             : } STRUCT_PACKED;
      34             : 
      35             : #define ROUTER_ADVERTISEMENT    134
      36             : #define NEIGHBOR_SOLICITATION   135
      37             : #define NEIGHBOR_ADVERTISEMENT  136
      38             : #define SOURCE_LL_ADDR          1
      39             : 
      40          11 : static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
      41             : {
      42             :         struct ip6addr *ip6addr;
      43             : 
      44          11 :         ip6addr = os_zalloc(sizeof(*ip6addr));
      45          11 :         if (!ip6addr)
      46           0 :                 return -1;
      47             : 
      48          11 :         os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
      49             : 
      50          11 :         dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
      51             : 
      52          11 :         return 0;
      53             : }
      54             : 
      55             : 
      56        2777 : void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
      57             : {
      58             :         struct ip6addr *ip6addr, *prev;
      59             : 
      60        2788 :         dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
      61             :                               list) {
      62          11 :                 hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
      63          11 :                 os_free(ip6addr);
      64             :         }
      65        2777 : }
      66             : 
      67             : 
      68          17 : static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
      69             : {
      70             :         struct ip6addr *ip6addr;
      71             : 
      72          22 :         dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
      73          22 :                 if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
      74          17 :                     ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
      75          12 :                     ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
      76           6 :                     ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
      77           6 :                         return 1;
      78             :         }
      79             : 
      80          11 :         return 0;
      81             : }
      82             : 
      83             : 
      84           3 : static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
      85             : {
      86             :         struct sta_info *sta;
      87             : 
      88           9 :         for (sta = hapd->sta_list; sta; sta = sta->next) {
      89           6 :                 if (!(sta->flags & WLAN_STA_AUTHORIZED))
      90           0 :                         continue;
      91           6 :                 x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
      92             :         }
      93           3 : }
      94             : 
      95             : 
      96          85 : static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
      97             :                          size_t len)
      98             : {
      99          85 :         struct hostapd_data *hapd = ctx;
     100             :         struct icmpv6_ndmsg *msg;
     101             :         struct in6_addr saddr;
     102             :         struct sta_info *sta;
     103             :         int res;
     104             :         char addrtxt[INET6_ADDRSTRLEN + 1];
     105             : 
     106          85 :         if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
     107          33 :                 return;
     108          85 :         msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
     109          85 :         switch (msg->icmp6h.icmp6_type) {
     110             :         case NEIGHBOR_SOLICITATION:
     111          52 :                 if (len < ETH_HLEN + sizeof(*msg))
     112          13 :                         return;
     113          39 :                 if (msg->opt_type != SOURCE_LL_ADDR)
     114           2 :                         return;
     115             : 
     116             :                 /*
     117             :                  * IPv6 header may not be 32-bit aligned in the buffer, so use
     118             :                  * a local copy to avoid unaligned reads.
     119             :                  */
     120          37 :                 os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
     121          53 :                 if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
     122          16 :                       saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
     123          29 :                         if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
     124           2 :                                 return;
     125          27 :                         sta = ap_get_sta(hapd, msg->opt_lladdr);
     126          27 :                         if (!sta)
     127          10 :                                 return;
     128             : 
     129          17 :                         if (sta_has_ip6addr(sta, &saddr))
     130           6 :                                 return;
     131             : 
     132          11 :                         if (inet_ntop(AF_INET6, &saddr, addrtxt,
     133             :                                       sizeof(addrtxt)) == NULL)
     134           0 :                                 addrtxt[0] = '\0';
     135          66 :                         wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
     136          66 :                                    MACSTR, addrtxt, MAC2STR(sta->addr));
     137          11 :                         hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
     138          11 :                         res = hostapd_drv_br_add_ip_neigh(hapd, 6,
     139             :                                                           (u8 *) &saddr,
     140          11 :                                                           128, sta->addr);
     141          11 :                         if (res) {
     142           0 :                                 wpa_printf(MSG_ERROR,
     143             :                                            "ndisc_snoop: Adding ip neigh failed: %d",
     144             :                                            res);
     145           0 :                                 return;
     146             :                         }
     147             : 
     148          11 :                         if (sta_ip6addr_add(sta, &saddr))
     149           0 :                                 return;
     150             :                 }
     151          19 :                 break;
     152             :         case ROUTER_ADVERTISEMENT:
     153           2 :                 if (hapd->conf->disable_dgaf)
     154           1 :                         ucast_to_stas(hapd, buf, len);
     155           2 :                 break;
     156             :         case NEIGHBOR_ADVERTISEMENT:
     157           8 :                 if (hapd->conf->na_mcast_to_ucast)
     158           2 :                         ucast_to_stas(hapd, buf, len);
     159           8 :                 break;
     160             :         default:
     161          23 :                 break;
     162             :         }
     163             : }
     164             : 
     165             : 
     166           5 : int ndisc_snoop_init(struct hostapd_data *hapd)
     167             : {
     168           5 :         hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
     169             :                                                  L2_PACKET_FILTER_NDISC);
     170           5 :         if (hapd->sock_ndisc == NULL) {
     171           0 :                 wpa_printf(MSG_DEBUG,
     172             :                            "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
     173           0 :                            strerror(errno));
     174           0 :                 return -1;
     175             :         }
     176             : 
     177           5 :         return 0;
     178             : }
     179             : 
     180             : 
     181        1682 : void ndisc_snoop_deinit(struct hostapd_data *hapd)
     182             : {
     183        1682 :         l2_packet_deinit(hapd->sock_ndisc);
     184        1682 : }

Generated by: LCOV version 1.10