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

Generated by: LCOV version 1.10