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