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