/src/hostap/src/ap/eth_p_oui.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * hostapd / IEEE 802 OUI Extended EtherType 88-B7 |
3 | | * Copyright (c) 2016, Jouni Malinen <j@w1.fi> |
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 | | |
11 | | #include "utils/common.h" |
12 | | #include "utils/eloop.h" |
13 | | #include "l2_packet/l2_packet.h" |
14 | | #include "hostapd.h" |
15 | | #include "eth_p_oui.h" |
16 | | |
17 | | /* |
18 | | * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended |
19 | | * EtherType 88-B7. This file implements this with OUI 00:13:74 and |
20 | | * vendor-specific subtype 0x0001. |
21 | | */ |
22 | | static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 }; |
23 | | |
24 | | struct eth_p_oui_iface { |
25 | | struct dl_list list; |
26 | | char ifname[IFNAMSIZ + 1]; |
27 | | struct l2_packet_data *l2; |
28 | | struct dl_list receiver; |
29 | | }; |
30 | | |
31 | | struct eth_p_oui_ctx { |
32 | | struct dl_list list; |
33 | | struct eth_p_oui_iface *iface; |
34 | | /* all data needed to deliver and unregister */ |
35 | | u8 oui_suffix; /* last byte of OUI */ |
36 | | void (*rx_callback)(void *ctx, const u8 *src_addr, |
37 | | const u8 *dst_addr, u8 oui_suffix, |
38 | | const u8 *buf, size_t len); |
39 | | void *rx_callback_ctx; |
40 | | }; |
41 | | |
42 | | |
43 | | void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, |
44 | | const u8 *dst_addr, const u8 *buf, size_t len) |
45 | 0 | { |
46 | 0 | ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr, |
47 | 0 | ctx->oui_suffix, buf, len); |
48 | 0 | } |
49 | | |
50 | | |
51 | | static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) |
52 | 0 | { |
53 | 0 | struct eth_p_oui_iface *iface = ctx; |
54 | 0 | struct eth_p_oui_ctx *receiver; |
55 | 0 | const struct l2_ethhdr *ethhdr; |
56 | |
|
57 | 0 | if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) { |
58 | | /* too short packet */ |
59 | 0 | return; |
60 | 0 | } |
61 | | |
62 | 0 | ethhdr = (struct l2_ethhdr *) buf; |
63 | | /* trim eth_hdr from buf and len */ |
64 | 0 | buf += sizeof(*ethhdr); |
65 | 0 | len -= sizeof(*ethhdr); |
66 | | |
67 | | /* verify OUI and vendor-specific subtype match */ |
68 | 0 | if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0) |
69 | 0 | return; |
70 | 0 | buf += sizeof(global_oui); |
71 | 0 | len -= sizeof(global_oui); |
72 | |
|
73 | 0 | dl_list_for_each(receiver, &iface->receiver, |
74 | 0 | struct eth_p_oui_ctx, list) { |
75 | 0 | if (buf[0] != receiver->oui_suffix) |
76 | 0 | continue; |
77 | | |
78 | 0 | eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest, |
79 | 0 | buf + 1, len - 1); |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | | |
84 | | struct eth_p_oui_ctx * |
85 | | eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, |
86 | | void (*rx_callback)(void *ctx, const u8 *src_addr, |
87 | | const u8 *dst_addr, u8 oui_suffix, |
88 | | const u8 *buf, size_t len), |
89 | | void *rx_callback_ctx) |
90 | 0 | { |
91 | 0 | struct eth_p_oui_iface *iface; |
92 | 0 | struct eth_p_oui_ctx *receiver; |
93 | 0 | int found = 0; |
94 | 0 | struct hapd_interfaces *interfaces; |
95 | |
|
96 | 0 | receiver = os_zalloc(sizeof(*receiver)); |
97 | 0 | if (!receiver) |
98 | 0 | goto err; |
99 | | |
100 | 0 | receiver->oui_suffix = oui_suffix; |
101 | 0 | receiver->rx_callback = rx_callback; |
102 | 0 | receiver->rx_callback_ctx = rx_callback_ctx; |
103 | |
|
104 | 0 | interfaces = hapd->iface->interfaces; |
105 | |
|
106 | 0 | dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface, |
107 | 0 | list) { |
108 | 0 | if (os_strcmp(iface->ifname, ifname) != 0) |
109 | 0 | continue; |
110 | 0 | found = 1; |
111 | 0 | break; |
112 | 0 | } |
113 | |
|
114 | 0 | if (!found) { |
115 | 0 | iface = os_zalloc(sizeof(*iface)); |
116 | 0 | if (!iface) |
117 | 0 | goto err; |
118 | | |
119 | 0 | os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname)); |
120 | 0 | iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx, |
121 | 0 | iface, 1); |
122 | 0 | if (!iface->l2) { |
123 | 0 | os_free(iface); |
124 | 0 | goto err; |
125 | 0 | } |
126 | 0 | dl_list_init(&iface->receiver); |
127 | |
|
128 | 0 | dl_list_add_tail(&interfaces->eth_p_oui, &iface->list); |
129 | 0 | } |
130 | | |
131 | 0 | dl_list_add_tail(&iface->receiver, &receiver->list); |
132 | 0 | receiver->iface = iface; |
133 | |
|
134 | 0 | return receiver; |
135 | 0 | err: |
136 | 0 | os_free(receiver); |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | | |
141 | | void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx) |
142 | 0 | { |
143 | 0 | struct eth_p_oui_iface *iface; |
144 | |
|
145 | 0 | if (!ctx) |
146 | 0 | return; |
147 | | |
148 | 0 | iface = ctx->iface; |
149 | |
|
150 | 0 | dl_list_del(&ctx->list); |
151 | 0 | os_free(ctx); |
152 | |
|
153 | 0 | if (dl_list_empty(&iface->receiver)) { |
154 | 0 | dl_list_del(&iface->list); |
155 | 0 | l2_packet_deinit(iface->l2); |
156 | 0 | os_free(iface); |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | |
161 | | int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, |
162 | | const u8 *dst_addr, const u8 *buf, size_t len) |
163 | 0 | { |
164 | 0 | struct eth_p_oui_iface *iface = ctx->iface; |
165 | 0 | u8 *packet, *p; |
166 | 0 | size_t packet_len; |
167 | 0 | int ret; |
168 | 0 | struct l2_ethhdr *ethhdr; |
169 | |
|
170 | 0 | packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len; |
171 | 0 | packet = os_zalloc(packet_len); |
172 | 0 | if (!packet) |
173 | 0 | return -1; |
174 | 0 | p = packet; |
175 | |
|
176 | 0 | ethhdr = (struct l2_ethhdr *) packet; |
177 | 0 | os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN); |
178 | 0 | os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); |
179 | 0 | ethhdr->h_proto = host_to_be16(ETH_P_OUI); |
180 | 0 | p += sizeof(*ethhdr); |
181 | |
|
182 | 0 | os_memcpy(p, global_oui, sizeof(global_oui)); |
183 | 0 | p[sizeof(global_oui)] = ctx->oui_suffix; |
184 | 0 | p += sizeof(global_oui) + 1; |
185 | |
|
186 | 0 | os_memcpy(p, buf, len); |
187 | |
|
188 | 0 | ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len); |
189 | 0 | os_free(packet); |
190 | 0 | return ret; |
191 | 0 | } |