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 1426431149 Lines: 62 71 87.3 %
Date: 2015-03-15 Functions: 6 6 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        2013 : void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
      57             : {
      58             :         struct ip6addr *ip6addr, *prev;
      59             : 
      60        2024 :         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        2013 : }
      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          89 : static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
      85             :                          size_t len)
      86             : {
      87          89 :         struct hostapd_data *hapd = ctx;
      88             :         struct icmpv6_ndmsg *msg;
      89             :         struct in6_addr *saddr;
      90             :         struct sta_info *sta;
      91             :         int res;
      92             :         char addrtxt[INET6_ADDRSTRLEN + 1];
      93             : 
      94          89 :         if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
      95          39 :                 return;
      96          89 :         msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
      97          89 :         switch (msg->icmp6h.icmp6_type) {
      98             :         case NEIGHBOR_SOLICITATION:
      99          57 :                 if (len < ETH_HLEN + sizeof(*msg))
     100          12 :                         return;
     101          45 :                 if (msg->opt_type != SOURCE_LL_ADDR)
     102           2 :                         return;
     103             : 
     104          43 :                 saddr = &msg->ipv6h.ip6_src;
     105          59 :                 if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 &&
     106          16 :                       saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) {
     107          35 :                         if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
     108           2 :                                 return;
     109          33 :                         sta = ap_get_sta(hapd, msg->opt_lladdr);
     110          33 :                         if (!sta)
     111          16 :                                 return;
     112             : 
     113          17 :                         if (sta_has_ip6addr(sta, saddr))
     114           6 :                                 return;
     115             : 
     116          11 :                         if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
     117             :                             == NULL)
     118           0 :                                 addrtxt[0] = '\0';
     119          66 :                         wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
     120          66 :                                    MACSTR, addrtxt, MAC2STR(sta->addr));
     121          11 :                         hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
     122          11 :                         res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
     123          11 :                                                           128, sta->addr);
     124          11 :                         if (res) {
     125           0 :                                 wpa_printf(MSG_ERROR,
     126             :                                            "ndisc_snoop: Adding ip neigh failed: %d",
     127             :                                            res);
     128           0 :                                 return;
     129             :                         }
     130             : 
     131          11 :                         if (sta_ip6addr_add(sta, saddr))
     132           0 :                                 return;
     133             :                 }
     134          19 :                 break;
     135             :         case ROUTER_ADVERTISEMENT:
     136           2 :                 if (!hapd->conf->disable_dgaf)
     137           1 :                         return;
     138             :                 /* fall through */
     139             :         case NEIGHBOR_ADVERTISEMENT:
     140          39 :                 for (sta = hapd->sta_list; sta; sta = sta->next) {
     141          26 :                         if (!(sta->flags & WLAN_STA_AUTHORIZED))
     142           0 :                                 continue;
     143          26 :                         x_snoop_mcast_to_ucast_convert_send(hapd, sta,
     144             :                                                             (u8 *) buf, len);
     145             :                 }
     146          13 :                 break;
     147             :         default:
     148          18 :                 break;
     149             :         }
     150             : }
     151             : 
     152             : 
     153           5 : int ndisc_snoop_init(struct hostapd_data *hapd)
     154             : {
     155           5 :         hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
     156             :                                                  L2_PACKET_FILTER_NDISC);
     157           5 :         if (hapd->sock_ndisc == NULL) {
     158           0 :                 wpa_printf(MSG_DEBUG,
     159             :                            "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
     160           0 :                            strerror(errno));
     161           0 :                 return -1;
     162             :         }
     163             : 
     164           5 :         return 0;
     165             : }
     166             : 
     167             : 
     168        1043 : void ndisc_snoop_deinit(struct hostapd_data *hapd)
     169             : {
     170        1043 :         l2_packet_deinit(hapd->sock_ndisc);
     171        1043 : }

Generated by: LCOV version 1.10