Coverage Report

Created: 2025-08-26 06:04

/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
}