]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/ap/dhcp_snoop.c
AP: Extend the BSS bridge neighbor entry management to support IPv6
[thirdparty/hostap.git] / src / ap / dhcp_snoop.c
CommitLineData
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
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};
40
41#define DHCPACK 5
42static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
43
44
45static 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
134int 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
149void dhcp_snoop_deinit(struct hostapd_data *hapd)
150{
7d597d46
KP
151 l2_packet_deinit(hapd->sock_dhcp);
152}