16c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt/* 26c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt * Neighbor Discovery snooping for Proxy ARP 36c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt * Copyright (c) 2014, Qualcomm Atheros, Inc. 46c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt * 56c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt * This software may be distributed under the terms of the BSD license. 66c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt * See README for more details. 76c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt */ 86c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 96c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "utils/includes.h" 106c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include <netinet/ip6.h> 116c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include <netinet/icmp6.h> 126c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 136c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "utils/common.h" 146c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "l2_packet/l2_packet.h" 156c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "hostapd.h" 166c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "sta_info.h" 176c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "ap_drv_ops.h" 186c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "list.h" 196c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#include "x_snoop.h" 206c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 216c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtstruct ip6addr { 226c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct in6_addr addr; 236c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct dl_list list; 246c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt}; 256c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 266c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtstruct icmpv6_ndmsg { 276c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct ip6_hdr ipv6h; 286c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct icmp6_hdr icmp6h; 296c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct in6_addr target_addr; 306c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt u8 opt_type; 316c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt u8 len; 326c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt u8 opt_lladdr[0]; 336c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} STRUCT_PACKED; 346c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 356c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#define ROUTER_ADVERTISEMENT 134 366c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#define NEIGHBOR_SOLICITATION 135 376c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#define NEIGHBOR_ADVERTISEMENT 136 386c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt#define SOURCE_LL_ADDR 1 396c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 406c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtstatic int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr) 416c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 426c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct ip6addr *ip6addr; 436c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 446c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt ip6addr = os_zalloc(sizeof(*ip6addr)); 456c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (!ip6addr) 466c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return -1; 476c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 486c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt os_memcpy(&ip6addr->addr, addr, sizeof(*addr)); 496c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 506c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt dl_list_add_tail(&sta->ip6addr, &ip6addr->list); 516c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 526c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return 0; 536c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 546c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 556c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 566c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtvoid sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) 576c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 586c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct ip6addr *ip6addr, *prev; 596c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 606c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr, 616c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt list) { 626c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr); 636c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt os_free(ip6addr); 646c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 656c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 666c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 676c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 686c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtstatic int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) 696c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 706c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct ip6addr *ip6addr; 716c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 726c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) { 736c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] && 746c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] && 756c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] && 766c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3]) 776c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return 1; 786c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 796c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 806c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return 0; 816c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 826c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 836c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 841d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidtstatic void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len) 851d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt{ 861d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt struct sta_info *sta; 871d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt 881d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt for (sta = hapd->sta_list; sta; sta = sta->next) { 891d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt if (!(sta->flags & WLAN_STA_AUTHORIZED)) 901d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt continue; 911d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len); 921d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt } 931d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt} 941d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt 951d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt 966c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtstatic void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, 976c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt size_t len) 986c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 996c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct hostapd_data *hapd = ctx; 1006c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct icmpv6_ndmsg *msg; 1016c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct in6_addr *saddr; 1026c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt struct sta_info *sta; 1036c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt int res; 1046c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt char addrtxt[INET6_ADDRSTRLEN + 1]; 1056c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1066c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) 1076c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1086c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN]; 1096c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt switch (msg->icmp6h.icmp6_type) { 1106c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt case NEIGHBOR_SOLICITATION: 1116c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (len < ETH_HLEN + sizeof(*msg)) 1126c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1136c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (msg->opt_type != SOURCE_LL_ADDR) 1146c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1156c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1166c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt saddr = &msg->ipv6h.ip6_src; 1176c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && 1186c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { 1196c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) 1206c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1216c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt sta = ap_get_sta(hapd, msg->opt_lladdr); 1226c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (!sta) 1236c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1246c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1256c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (sta_has_ip6addr(sta, saddr)) 1266c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1276c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1286c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) 1296c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt == NULL) 1306c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt addrtxt[0] = '\0'; 1316c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " 1326c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt MACSTR, addrtxt, MAC2STR(sta->addr)); 1336c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); 1346c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, 1356c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 128, sta->addr); 1366c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (res) { 1376c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt wpa_printf(MSG_ERROR, 1386c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt "ndisc_snoop: Adding ip neigh failed: %d", 1396c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt res); 1406c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1416c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 1426c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1436c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (sta_ip6addr_add(sta, saddr)) 1446c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return; 1456c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 1466c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt break; 1476c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt case ROUTER_ADVERTISEMENT: 1481d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt if (hapd->conf->disable_dgaf) 1491d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt ucast_to_stas(hapd, buf, len); 1501d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt break; 1516c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt case NEIGHBOR_ADVERTISEMENT: 1521d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt if (hapd->conf->na_mcast_to_ucast) 1531d755d025b206e22b06aeb322e25a79f98ca7777Dmitry Shmidt ucast_to_stas(hapd, buf, len); 1546c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt break; 1556c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt default: 1566c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt break; 1576c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 1586c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 1596c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1606c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1616c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtint ndisc_snoop_init(struct hostapd_data *hapd) 1626c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 1636c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc, 1646c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt L2_PACKET_FILTER_NDISC); 1656c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt if (hapd->sock_ndisc == NULL) { 1666c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt wpa_printf(MSG_DEBUG, 1676c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s", 1686c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt strerror(errno)); 1696c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return -1; 1706c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt } 1716c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1726c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt return 0; 1736c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 1746c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1756c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt 1766c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidtvoid ndisc_snoop_deinit(struct hostapd_data *hapd) 1776c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt{ 1786c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt l2_packet_deinit(hapd->sock_ndisc); 1796c0da2bb83f6915d8260912362692d1a742e057bDmitry Shmidt} 180