]>
Commit | Line | Data |
---|---|---|
7d597d46 KP |
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" | |
7d597d46 KP |
10 | #include <linux/ip.h> |
11 | #include <linux/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" | |
1d783762 | 18 | #include "x_snoop.h" |
7d597d46 KP |
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 | }; | |
40 | ||
41 | #define DHCPACK 5 | |
42 | static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; | |
43 | ||
44 | ||
45 | static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, | |
46 | size_t len) | |
47 | { | |
48 | struct hostapd_data *hapd = ctx; | |
49 | const struct bootp_pkt *b; | |
50 | struct sta_info *sta; | |
51 | int exten_len; | |
52 | const u8 *end, *pos; | |
53 | int res, msgtype = 0, prefixlen = 32; | |
54 | u32 subnet_mask = 0; | |
55 | ||
56 | exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten)); | |
57 | if (exten_len < 4) | |
58 | return; | |
59 | ||
60 | b = (const struct bootp_pkt *) &buf[ETH_HLEN]; | |
61 | if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) | |
62 | return; | |
63 | ||
64 | /* Parse DHCP options */ | |
65 | end = (const u8 *) b + ntohs(b->iph.tot_len); | |
66 | pos = &b->exten[4]; | |
67 | while (pos < end && *pos != 0xff) { | |
68 | const u8 *opt = pos++; | |
69 | ||
70 | if (*opt == 0) /* padding */ | |
71 | continue; | |
72 | ||
73 | pos += *pos + 1; | |
74 | if (pos >= end) | |
75 | break; | |
76 | ||
77 | switch (*opt) { | |
78 | case 1: /* subnet mask */ | |
79 | if (opt[1] == 4) | |
80 | subnet_mask = WPA_GET_BE32(&opt[2]); | |
81 | if (subnet_mask == 0) | |
82 | return; | |
83 | while (!(subnet_mask & 0x1)) { | |
84 | subnet_mask >>= 1; | |
85 | prefixlen--; | |
86 | } | |
87 | break; | |
88 | case 53: /* message type */ | |
89 | if (opt[1]) | |
90 | msgtype = opt[2]; | |
91 | break; | |
92 | default: | |
93 | break; | |
94 | } | |
95 | } | |
96 | ||
97 | if (msgtype == DHCPACK) { | |
98 | if (b->your_ip == 0) | |
99 | return; | |
100 | ||
101 | /* DHCPACK for DHCPREQUEST */ | |
102 | sta = ap_get_sta(hapd, b->hw_addr); | |
103 | if (!sta) | |
104 | return; | |
105 | ||
106 | wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR | |
107 | " @ IPv4 address %X/%d", | |
108 | MAC2STR(sta->addr), ntohl(b->your_ip), prefixlen); | |
109 | ||
110 | if (sta->ipaddr == b->your_ip) | |
111 | return; | |
112 | ||
113 | if (sta->ipaddr != 0) { | |
114 | wpa_printf(MSG_DEBUG, | |
115 | "dhcp_snoop: Removing IPv4 address %X from the ip neigh table", | |
116 | sta->ipaddr); | |
ed4ddb6d KP |
117 | hostapd_drv_br_delete_ip_neigh(hapd, 4, |
118 | (u8 *) &sta->ipaddr); | |
7d597d46 KP |
119 | } |
120 | ||
ed4ddb6d KP |
121 | res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip, |
122 | prefixlen, sta->addr); | |
7d597d46 KP |
123 | if (res) { |
124 | wpa_printf(MSG_DEBUG, | |
125 | "dhcp_snoop: Adding ip neigh table failed: %d", | |
126 | res); | |
127 | return; | |
128 | } | |
129 | sta->ipaddr = b->your_ip; | |
130 | } | |
131 | } | |
132 | ||
133 | ||
134 | int dhcp_snoop_init(struct hostapd_data *hapd) | |
135 | { | |
1d783762 KP |
136 | hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp, |
137 | L2_PACKET_FILTER_DHCP); | |
7d597d46 KP |
138 | if (hapd->sock_dhcp == NULL) { |
139 | wpa_printf(MSG_DEBUG, | |
140 | "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s", | |
141 | strerror(errno)); | |
142 | return -1; | |
143 | } | |
144 | ||
7d597d46 KP |
145 | return 0; |
146 | } | |
147 | ||
148 | ||
149 | void dhcp_snoop_deinit(struct hostapd_data *hapd) | |
150 | { | |
7d597d46 KP |
151 | l2_packet_deinit(hapd->sock_dhcp); |
152 | } |