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