1281681Srpaulo/* 2281681Srpaulo * DHCP snooping for Proxy ARP 3281681Srpaulo * Copyright (c) 2014, Qualcomm Atheros, Inc. 4281681Srpaulo * 5281681Srpaulo * This software may be distributed under the terms of the BSD license. 6281681Srpaulo * See README for more details. 7281681Srpaulo */ 8281681Srpaulo 9281681Srpaulo#include "utils/includes.h" 10281681Srpaulo 11281681Srpaulo#include "utils/common.h" 12346981Scy#include "common/dhcp.h" 13281681Srpaulo#include "l2_packet/l2_packet.h" 14281681Srpaulo#include "hostapd.h" 15281681Srpaulo#include "sta_info.h" 16281681Srpaulo#include "ap_drv_ops.h" 17281681Srpaulo#include "x_snoop.h" 18281681Srpaulo#include "dhcp_snoop.h" 19281681Srpaulo 20281681Srpaulo 21281681Srpaulostatic const char * ipaddr_str(u32 addr) 22281681Srpaulo{ 23281681Srpaulo static char buf[17]; 24281681Srpaulo 25281681Srpaulo os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u", 26281681Srpaulo (addr >> 24) & 0xff, (addr >> 16) & 0xff, 27281681Srpaulo (addr >> 8) & 0xff, addr & 0xff); 28281681Srpaulo return buf; 29281681Srpaulo} 30281681Srpaulo 31281681Srpaulo 32281681Srpaulostatic void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, 33281681Srpaulo size_t len) 34281681Srpaulo{ 35281681Srpaulo struct hostapd_data *hapd = ctx; 36281681Srpaulo const struct bootp_pkt *b; 37281681Srpaulo struct sta_info *sta; 38281681Srpaulo int exten_len; 39281681Srpaulo const u8 *end, *pos; 40281681Srpaulo int res, msgtype = 0, prefixlen = 32; 41281681Srpaulo u32 subnet_mask = 0; 42281681Srpaulo u16 tot_len; 43281681Srpaulo 44281681Srpaulo exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten)); 45281681Srpaulo if (exten_len < 4) 46281681Srpaulo return; 47281681Srpaulo 48281681Srpaulo b = (const struct bootp_pkt *) &buf[ETH_HLEN]; 49281681Srpaulo tot_len = ntohs(b->iph.tot_len); 50281681Srpaulo if (tot_len > (unsigned int) (len - ETH_HLEN)) 51281681Srpaulo return; 52281681Srpaulo 53346981Scy if (WPA_GET_BE32(b->exten) != DHCP_MAGIC) 54281681Srpaulo return; 55281681Srpaulo 56281681Srpaulo /* Parse DHCP options */ 57281681Srpaulo end = (const u8 *) b + tot_len; 58281681Srpaulo pos = &b->exten[4]; 59346981Scy while (pos < end && *pos != DHCP_OPT_END) { 60281681Srpaulo const u8 *opt = pos++; 61281681Srpaulo 62346981Scy if (*opt == DHCP_OPT_PAD) 63281681Srpaulo continue; 64281681Srpaulo 65346981Scy if (pos >= end || 1 + *pos > end - pos) 66346981Scy break; 67281681Srpaulo pos += *pos + 1; 68281681Srpaulo if (pos >= end) 69281681Srpaulo break; 70281681Srpaulo 71281681Srpaulo switch (*opt) { 72346981Scy case DHCP_OPT_SUBNET_MASK: 73281681Srpaulo if (opt[1] == 4) 74281681Srpaulo subnet_mask = WPA_GET_BE32(&opt[2]); 75281681Srpaulo if (subnet_mask == 0) 76281681Srpaulo return; 77281681Srpaulo while (!(subnet_mask & 0x1)) { 78281681Srpaulo subnet_mask >>= 1; 79281681Srpaulo prefixlen--; 80281681Srpaulo } 81281681Srpaulo break; 82346981Scy case DHCP_OPT_MSG_TYPE: 83281681Srpaulo if (opt[1]) 84281681Srpaulo msgtype = opt[2]; 85281681Srpaulo break; 86281681Srpaulo default: 87281681Srpaulo break; 88281681Srpaulo } 89281681Srpaulo } 90281681Srpaulo 91346981Scy if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { 92346981Scy for (sta = hapd->sta_list; sta; sta = sta->next) { 93346981Scy if (!(sta->flags & WLAN_STA_AUTHORIZED)) 94346981Scy continue; 95346981Scy x_snoop_mcast_to_ucast_convert_send(hapd, sta, 96346981Scy (u8 *) buf, len); 97346981Scy } 98346981Scy } 99346981Scy 100281681Srpaulo if (msgtype == DHCPACK) { 101281681Srpaulo if (b->your_ip == 0) 102281681Srpaulo return; 103281681Srpaulo 104281681Srpaulo /* DHCPACK for DHCPREQUEST */ 105281681Srpaulo sta = ap_get_sta(hapd, b->hw_addr); 106281681Srpaulo if (!sta) 107281681Srpaulo return; 108281681Srpaulo 109281681Srpaulo wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR 110281681Srpaulo " @ IPv4 address %s/%d", 111337817Scy MAC2STR(sta->addr), 112337817Scy ipaddr_str(be_to_host32(b->your_ip)), 113281681Srpaulo prefixlen); 114281681Srpaulo 115281681Srpaulo if (sta->ipaddr == b->your_ip) 116281681Srpaulo return; 117281681Srpaulo 118281681Srpaulo if (sta->ipaddr != 0) { 119281681Srpaulo wpa_printf(MSG_DEBUG, 120281681Srpaulo "dhcp_snoop: Removing IPv4 address %s from the ip neigh table", 121281681Srpaulo ipaddr_str(be_to_host32(sta->ipaddr))); 122281681Srpaulo hostapd_drv_br_delete_ip_neigh(hapd, 4, 123281681Srpaulo (u8 *) &sta->ipaddr); 124281681Srpaulo } 125281681Srpaulo 126281681Srpaulo res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip, 127281681Srpaulo prefixlen, sta->addr); 128281681Srpaulo if (res) { 129281681Srpaulo wpa_printf(MSG_DEBUG, 130281681Srpaulo "dhcp_snoop: Adding ip neigh table failed: %d", 131281681Srpaulo res); 132281681Srpaulo return; 133281681Srpaulo } 134281681Srpaulo sta->ipaddr = b->your_ip; 135281681Srpaulo } 136281681Srpaulo} 137281681Srpaulo 138281681Srpaulo 139281681Srpauloint dhcp_snoop_init(struct hostapd_data *hapd) 140281681Srpaulo{ 141281681Srpaulo hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp, 142281681Srpaulo L2_PACKET_FILTER_DHCP); 143281681Srpaulo if (hapd->sock_dhcp == NULL) { 144281681Srpaulo wpa_printf(MSG_DEBUG, 145281681Srpaulo "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s", 146281681Srpaulo strerror(errno)); 147281681Srpaulo return -1; 148281681Srpaulo } 149281681Srpaulo 150281681Srpaulo return 0; 151281681Srpaulo} 152281681Srpaulo 153281681Srpaulo 154281681Srpaulovoid dhcp_snoop_deinit(struct hostapd_data *hapd) 155281681Srpaulo{ 156281681Srpaulo l2_packet_deinit(hapd->sock_dhcp); 157346981Scy hapd->sock_dhcp = NULL; 158281681Srpaulo} 159