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