dhcp_snoop.c revision 281806
1/*
2 * DHCP 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/ip.h>
11#include <netinet/udp.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 "x_snoop.h"
19#include "dhcp_snoop.h"
20
21struct bootp_pkt {
22	struct iphdr iph;
23	struct udphdr udph;
24	u8 op;
25	u8 htype;
26	u8 hlen;
27	u8 hops;
28	be32 xid;
29	be16 secs;
30	be16 flags;
31	be32 client_ip;
32	be32 your_ip;
33	be32 server_ip;
34	be32 relay_ip;
35	u8 hw_addr[16];
36	u8 serv_name[64];
37	u8 boot_file[128];
38	u8 exten[312];
39} STRUCT_PACKED;
40
41#define DHCPACK	5
42static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
43
44
45static const char * ipaddr_str(u32 addr)
46{
47	static char buf[17];
48
49	os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
50		    (addr >> 24) & 0xff, (addr >> 16) & 0xff,
51		    (addr >> 8) & 0xff, addr & 0xff);
52	return buf;
53}
54
55
56static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
57			size_t len)
58{
59	struct hostapd_data *hapd = ctx;
60	const struct bootp_pkt *b;
61	struct sta_info *sta;
62	int exten_len;
63	const u8 *end, *pos;
64	int res, msgtype = 0, prefixlen = 32;
65	u32 subnet_mask = 0;
66	u16 tot_len;
67
68	exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
69	if (exten_len < 4)
70		return;
71
72	b = (const struct bootp_pkt *) &buf[ETH_HLEN];
73	tot_len = ntohs(b->iph.tot_len);
74	if (tot_len > (unsigned int) (len - ETH_HLEN))
75		return;
76
77	if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
78		return;
79
80	/* Parse DHCP options */
81	end = (const u8 *) b + tot_len;
82	pos = &b->exten[4];
83	while (pos < end && *pos != 0xff) {
84		const u8 *opt = pos++;
85
86		if (*opt == 0) /* padding */
87			continue;
88
89		pos += *pos + 1;
90		if (pos >= end)
91			break;
92
93		switch (*opt) {
94		case 1:  /* subnet mask */
95			if (opt[1] == 4)
96				subnet_mask = WPA_GET_BE32(&opt[2]);
97			if (subnet_mask == 0)
98				return;
99			while (!(subnet_mask & 0x1)) {
100				subnet_mask >>= 1;
101				prefixlen--;
102			}
103			break;
104		case 53: /* message type */
105			if (opt[1])
106				msgtype = opt[2];
107			break;
108		default:
109			break;
110		}
111	}
112
113	if (msgtype == DHCPACK) {
114		if (b->your_ip == 0)
115			return;
116
117		/* DHCPACK for DHCPREQUEST */
118		sta = ap_get_sta(hapd, b->hw_addr);
119		if (!sta)
120			return;
121
122		wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
123			   " @ IPv4 address %s/%d",
124			   MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
125			   prefixlen);
126
127		if (sta->ipaddr == b->your_ip)
128			return;
129
130		if (sta->ipaddr != 0) {
131			wpa_printf(MSG_DEBUG,
132				   "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
133				   ipaddr_str(be_to_host32(sta->ipaddr)));
134			hostapd_drv_br_delete_ip_neigh(hapd, 4,
135						       (u8 *) &sta->ipaddr);
136		}
137
138		res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
139						  prefixlen, sta->addr);
140		if (res) {
141			wpa_printf(MSG_DEBUG,
142				   "dhcp_snoop: Adding ip neigh table failed: %d",
143				   res);
144			return;
145		}
146		sta->ipaddr = b->your_ip;
147	}
148
149	if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
150		for (sta = hapd->sta_list; sta; sta = sta->next) {
151			if (!(sta->flags & WLAN_STA_AUTHORIZED))
152				continue;
153			x_snoop_mcast_to_ucast_convert_send(hapd, sta,
154							    (u8 *) buf, len);
155		}
156	}
157}
158
159
160int dhcp_snoop_init(struct hostapd_data *hapd)
161{
162	hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
163						L2_PACKET_FILTER_DHCP);
164	if (hapd->sock_dhcp == NULL) {
165		wpa_printf(MSG_DEBUG,
166			   "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
167			   strerror(errno));
168		return -1;
169	}
170
171	return 0;
172}
173
174
175void dhcp_snoop_deinit(struct hostapd_data *hapd)
176{
177	l2_packet_deinit(hapd->sock_dhcp);
178}
179