/src/wireshark/epan/manuf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* manuf.c |
2 | | * |
3 | | * Wireshark - Network traffic analyzer |
4 | | * By Gerald Combs <gerald@wireshark.org> |
5 | | * Copyright 1998 Gerald Combs |
6 | | * |
7 | | * SPDX-License-Identifier: GPL-2.0-or-later |
8 | | */ |
9 | | |
10 | | #include "manuf.h" |
11 | | #include <stdlib.h> |
12 | | |
13 | | // MA-L / OUI - MAC Address Block Large (24-bit prefix) |
14 | 87.4k | #define MA_L 0 |
15 | | // MA-M - MAC Address Block Medium (28-bit prefix) |
16 | 933 | #define MA_M 1 |
17 | | // MA-S / OUI-36 - MAC Address Block Small (36-bit prefix) |
18 | 201 | #define MA_S 2 |
19 | | |
20 | | typedef struct { |
21 | | uint8_t oui24[3]; |
22 | | /* Identifies the 3-byte prefix as part of MA-M or MA-S (or MA-L if none of those). */ |
23 | | uint8_t kind; |
24 | | } manuf_registry_t; |
25 | | |
26 | | typedef struct { |
27 | | uint8_t oui24[3]; |
28 | | const char *short_name; |
29 | | const char *long_name; |
30 | | } manuf_oui24_t; |
31 | | |
32 | | typedef struct { |
33 | | uint8_t oui28[4]; |
34 | | const char *short_name; |
35 | | const char *long_name; |
36 | | } manuf_oui28_t; |
37 | | |
38 | | typedef struct { |
39 | | uint8_t oui36[5]; |
40 | | const char *short_name; |
41 | | const char *long_name; |
42 | | } manuf_oui36_t; |
43 | | |
44 | | #include "manuf-data.c" |
45 | | |
46 | | static int |
47 | | compare_oui24_registry(const void *key, const void *element) |
48 | 384k | { |
49 | 384k | const uint8_t *addr = (const uint8_t *)key; |
50 | 384k | const manuf_registry_t *entry = (const manuf_registry_t *)element; |
51 | | |
52 | 384k | return memcmp(addr, entry->oui24, 3); |
53 | 384k | } |
54 | | |
55 | | static int |
56 | | compare_oui24_entry(const void *key, const void *element) |
57 | 665k | { |
58 | 665k | const uint8_t *addr = (const uint8_t *)key; |
59 | 665k | const manuf_oui24_t *oui = (const manuf_oui24_t *)element; |
60 | | |
61 | 665k | return memcmp(addr, oui->oui24, 3); |
62 | 665k | } |
63 | | |
64 | | static int |
65 | | compare_oui28_entry(const void *key, const void *element) |
66 | 9.53k | { |
67 | 9.53k | const uint8_t *addr = (const uint8_t *)key; |
68 | 9.53k | const manuf_oui28_t *oui = (const manuf_oui28_t *)element; |
69 | | |
70 | | // The caller is expected to have masked out (addr[3] & 0xF0). |
71 | 9.53k | return memcmp(addr, oui->oui28, 4); |
72 | 9.53k | } |
73 | | |
74 | | static int |
75 | | compare_oui36_entry(const void *key, const void *element) |
76 | 1.28k | { |
77 | 1.28k | const uint8_t *addr = (const uint8_t *)key; |
78 | 1.28k | const manuf_oui36_t *oui = (const manuf_oui36_t *)element; |
79 | | |
80 | | // The caller is expected to have masked out (addr[4] & 0xF0). |
81 | 1.28k | return memcmp(addr, oui->oui36, 5); |
82 | 1.28k | } |
83 | | |
84 | | static int |
85 | | select_registry(const uint8_t addr[6]) |
86 | 44.7k | { |
87 | 44.7k | const manuf_registry_t *entry; |
88 | | |
89 | 44.7k | entry = bsearch(addr, ieee_registry_table, G_N_ELEMENTS(ieee_registry_table), sizeof(manuf_registry_t), compare_oui24_registry); |
90 | 44.7k | if (entry) |
91 | 1.05k | return entry->kind; |
92 | 43.7k | return MA_L; |
93 | 44.7k | } |
94 | | |
95 | | static const manuf_oui24_t * |
96 | | manuf_oui24_lookup(const uint8_t addr[6]) |
97 | 43.7k | { |
98 | 43.7k | return bsearch(addr, global_manuf_oui24_table, |
99 | 43.7k | G_N_ELEMENTS(global_manuf_oui24_table), |
100 | 43.7k | sizeof(manuf_oui24_t), |
101 | 43.7k | compare_oui24_entry); |
102 | 43.7k | } |
103 | | |
104 | | static const manuf_oui28_t * |
105 | | manuf_oui28_lookup(const uint8_t addr[6]) |
106 | 849 | { |
107 | 849 | const uint8_t addr28[6] = { addr[0], addr[1], addr[2], addr[3] & 0xF0, }; |
108 | 849 | return bsearch(addr28, global_manuf_oui28_table, |
109 | 849 | G_N_ELEMENTS(global_manuf_oui28_table), |
110 | 849 | sizeof(manuf_oui28_t), |
111 | 849 | compare_oui28_entry); |
112 | 849 | } |
113 | | |
114 | | static const manuf_oui36_t * |
115 | | manuf_oui36_lookup(const uint8_t addr[6]) |
116 | 110 | { |
117 | 110 | const uint8_t addr36[6] = { addr[0], addr[1], addr[2], addr[3], addr[4] & 0xF0, }; |
118 | 110 | return bsearch(addr36, global_manuf_oui36_table, |
119 | 110 | G_N_ELEMENTS(global_manuf_oui36_table), |
120 | 110 | sizeof(manuf_oui36_t), |
121 | 110 | compare_oui36_entry); |
122 | 110 | } |
123 | | |
124 | | const char * |
125 | | ws_manuf_lookup(const uint8_t addr[6], const char **long_name_ptr, unsigned *mask_ptr) |
126 | 37.4k | { |
127 | 37.4k | uint8_t addr_copy[6]; |
128 | 37.4k | memcpy(addr_copy, addr, 6); |
129 | | /* Mask out the broadcast/multicast flag */ |
130 | 37.4k | addr_copy[0] &= 0xFE; |
131 | | |
132 | 37.4k | const char *short_name = NULL, *long_name = NULL; |
133 | 37.4k | unsigned mask = 0; |
134 | | |
135 | 37.4k | switch (select_registry(addr_copy)) { |
136 | 36.5k | case MA_L: |
137 | 36.5k | { |
138 | 36.5k | const manuf_oui24_t *ptr = manuf_oui24_lookup(addr_copy); |
139 | 36.5k | if (ptr) { |
140 | 0 | short_name = ptr->short_name; |
141 | 0 | long_name = ptr->long_name; |
142 | 0 | mask = 24; |
143 | 0 | } |
144 | 36.5k | break; |
145 | 0 | } |
146 | 849 | case MA_M: |
147 | 849 | { |
148 | 849 | const manuf_oui28_t *ptr = manuf_oui28_lookup(addr_copy); |
149 | 849 | if (ptr) { |
150 | 804 | short_name = ptr->short_name; |
151 | 804 | long_name = ptr->long_name; |
152 | 804 | mask = 28; |
153 | 804 | } |
154 | 849 | break; |
155 | 0 | } |
156 | 110 | case MA_S: |
157 | 110 | { |
158 | 110 | const manuf_oui36_t *ptr = manuf_oui36_lookup(addr_copy); |
159 | 110 | if (ptr) { |
160 | 75 | short_name = ptr->short_name; |
161 | 75 | long_name = ptr->long_name; |
162 | 75 | mask = 36; |
163 | 75 | } |
164 | 110 | break; |
165 | 0 | } |
166 | 0 | default: |
167 | 0 | ws_assert_not_reached(); |
168 | 37.4k | } |
169 | | |
170 | 37.4k | if (mask_ptr) { |
171 | 8.01k | *mask_ptr = mask; |
172 | 8.01k | } |
173 | 37.4k | if (long_name_ptr) { |
174 | 37.4k | *long_name_ptr = long_name; |
175 | 37.4k | } |
176 | 37.4k | return short_name; |
177 | 37.4k | } |
178 | | |
179 | | const char * |
180 | | ws_manuf_lookup_str(const uint8_t addr[6], const char **long_name_ptr) |
181 | 29.4k | { |
182 | 29.4k | return ws_manuf_lookup(addr, long_name_ptr, NULL); |
183 | 29.4k | } |
184 | | |
185 | | const char * |
186 | | ws_manuf_lookup_oui24(const uint8_t oui[3], const char **long_name_ptr) |
187 | 7.27k | { |
188 | 7.27k | uint8_t addr_copy[6] = {0}; |
189 | 7.27k | memcpy(addr_copy, oui, 3); |
190 | | /* Mask out the broadcast/multicast flag */ |
191 | 7.27k | addr_copy[0] &= 0xFE; |
192 | | |
193 | 7.27k | const char *short_name = NULL, *long_name = NULL; |
194 | | |
195 | 7.27k | switch (select_registry(addr_copy)) { |
196 | 7.18k | case MA_L: |
197 | 7.18k | { |
198 | 7.18k | const manuf_oui24_t *ptr = manuf_oui24_lookup(addr_copy); |
199 | 7.18k | if (ptr) { |
200 | 801 | short_name = ptr->short_name; |
201 | 801 | long_name = ptr->long_name; |
202 | 801 | } |
203 | 7.18k | break; |
204 | 0 | } |
205 | 84 | case MA_M: |
206 | 91 | case MA_S: |
207 | 91 | { |
208 | | /* XXX: These are officially registered to |
209 | | * "IEEE Registration Authority" and we could return that, but |
210 | | * we'd have to change expectatins elsewhere in the code. |
211 | | */ |
212 | 91 | break; |
213 | 84 | } |
214 | 0 | default: |
215 | 0 | ws_assert_not_reached(); |
216 | 7.27k | } |
217 | | |
218 | 7.27k | if (long_name_ptr) { |
219 | 7.27k | *long_name_ptr = long_name; |
220 | 7.27k | } |
221 | 7.27k | return short_name; |
222 | 7.27k | } |
223 | | |
224 | | static inline struct ws_manuf * |
225 | | copy_oui24(struct ws_manuf *dst, const manuf_oui24_t *src) |
226 | 0 | { |
227 | 0 | memcpy(dst->block, src->oui24, sizeof(src->oui24)); |
228 | 0 | dst->block[3] = 0; |
229 | 0 | dst->block[4] = 0; |
230 | 0 | dst->mask = 24; |
231 | 0 | dst->short_name = src->short_name; |
232 | 0 | dst->long_name = src->long_name; |
233 | 0 | return dst; |
234 | 0 | } |
235 | | |
236 | | static inline struct ws_manuf * |
237 | | copy_oui28(struct ws_manuf *dst, const manuf_oui28_t *src) |
238 | 0 | { |
239 | 0 | memcpy(dst->block, src->oui28, sizeof(src->oui28)); |
240 | 0 | dst->block[4] = 0; |
241 | 0 | dst->mask = 28; |
242 | 0 | dst->short_name = src->short_name; |
243 | 0 | dst->long_name = src->long_name; |
244 | 0 | return dst; |
245 | 0 | } |
246 | | |
247 | | static inline struct ws_manuf * |
248 | | copy_oui36(struct ws_manuf *dst, const manuf_oui36_t *src) |
249 | 0 | { |
250 | 0 | memcpy(dst->block, src->oui36, sizeof(src->oui36)); |
251 | 0 | dst->mask = 36; |
252 | 0 | dst->short_name = src->short_name; |
253 | 0 | dst->long_name = src->long_name; |
254 | 0 | return dst; |
255 | 0 | } |
256 | | |
257 | | void |
258 | | ws_manuf_iter_init(ws_manuf_iter_t *iter) |
259 | 0 | { |
260 | 0 | iter->idx24 = 0; |
261 | 0 | copy_oui24(&iter->buf24, &global_manuf_oui24_table[iter->idx24]); |
262 | 0 | iter->idx28 = 0; |
263 | 0 | copy_oui28(&iter->buf28, &global_manuf_oui28_table[iter->idx28]); |
264 | 0 | iter->idx36 = 0; |
265 | 0 | copy_oui36(&iter->buf36, &global_manuf_oui36_table[iter->idx36]); |
266 | 0 | } |
267 | | |
268 | | /** |
269 | | * Iterate between 3 registries in ascending order. This is not the same as |
270 | | * fully iterating through one registry followed by another. For example, after |
271 | | * visiting "00:55:B1", it could go to "00:55:DA:00/28", and eventually end up |
272 | | * at "00:56:2B" again. |
273 | | * |
274 | | * The "iter" structure must be zero initialized before the first iteration. |
275 | | */ |
276 | | bool |
277 | | ws_manuf_iter_next(ws_manuf_iter_t *iter, struct ws_manuf *result) |
278 | 0 | { |
279 | 0 | struct ws_manuf *vector[3] = { NULL, NULL, NULL }; |
280 | 0 | size_t idx = 0; |
281 | 0 | struct ws_manuf *ptr; |
282 | | |
283 | | /* Read current positions. */ |
284 | 0 | if (iter->idx24 < G_N_ELEMENTS(global_manuf_oui24_table)) { |
285 | 0 | vector[idx++] = &iter->buf24; |
286 | 0 | } |
287 | 0 | if (iter->idx28 < G_N_ELEMENTS(global_manuf_oui28_table)) { |
288 | 0 | vector[idx++] = &iter->buf28; |
289 | 0 | } |
290 | 0 | if (iter->idx36 < G_N_ELEMENTS(global_manuf_oui36_table)) { |
291 | 0 | vector[idx++] = &iter->buf36; |
292 | 0 | } |
293 | | |
294 | | /* None remaining, we're done. */ |
295 | 0 | if (idx == 0) |
296 | 0 | return false; |
297 | | |
298 | | /* Select smallest current prefix out of the 3 registries. |
299 | | * There is at least one entry and index 0 is non-empty. */ |
300 | 0 | ptr = vector[0]; |
301 | 0 | for (size_t i = 1; i < idx; i++) { |
302 | 0 | if (vector[i] && memcmp(vector[i]->block, ptr->block, MANUF_BLOCK_SIZE) < 0) { |
303 | 0 | ptr = vector[i]; |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | | /* We have the next smallest element, return result. */ |
308 | 0 | memcpy(result, ptr, sizeof(struct ws_manuf)); |
309 | | |
310 | | /* Advance iterator and copy new element. */ |
311 | 0 | if (ptr->mask == 24) { |
312 | 0 | iter->idx24++; |
313 | 0 | if (iter->idx24 < G_N_ELEMENTS(global_manuf_oui24_table)) { |
314 | 0 | copy_oui24(&iter->buf24, &global_manuf_oui24_table[iter->idx24]); |
315 | 0 | } |
316 | 0 | } |
317 | 0 | else if (ptr->mask == 28) { |
318 | 0 | iter->idx28++; |
319 | 0 | if (iter->idx28 < G_N_ELEMENTS(global_manuf_oui28_table)) { |
320 | 0 | copy_oui28(&iter->buf28, &global_manuf_oui28_table[iter->idx28]); |
321 | 0 | } |
322 | 0 | } |
323 | 0 | else if (ptr->mask == 36) { |
324 | 0 | iter->idx36++; |
325 | 0 | if (iter->idx36 < G_N_ELEMENTS(global_manuf_oui36_table)) { |
326 | 0 | copy_oui36(&iter->buf36, &global_manuf_oui36_table[iter->idx36]); |
327 | 0 | } |
328 | 0 | } |
329 | 0 | else |
330 | 0 | ws_assert_not_reached(); |
331 | |
|
332 | 0 | return true; |
333 | 0 | } |
334 | | |
335 | | const char * |
336 | | ws_manuf_block_str(char *buf, size_t buf_size, const struct ws_manuf *ptr) |
337 | 0 | { |
338 | 0 | if (ptr->mask == 24) { |
339 | | /* The mask is implied as the full 24 bits when printing a traditional OUI.*/ |
340 | 0 | snprintf(buf, buf_size, "%02"PRIX8":%02"PRIX8":%02"PRIX8, |
341 | 0 | ptr->block[0], ptr->block[1], ptr->block[2]); |
342 | 0 | } |
343 | 0 | else if (ptr->mask == 28) { |
344 | 0 | snprintf(buf, buf_size, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8"/28", |
345 | 0 | ptr->block[0], ptr->block[1], ptr->block[2], ptr->block[3]); |
346 | 0 | } |
347 | 0 | else if (ptr->mask == 36) { |
348 | 0 | snprintf(buf, buf_size, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8"/36", |
349 | 0 | ptr->block[0], ptr->block[1], ptr->block[2], ptr->block[3], ptr->block[4]); |
350 | 0 | } |
351 | 0 | else { |
352 | 0 | ws_assert_not_reached(); |
353 | 0 | } |
354 | |
|
355 | 0 | return buf; |
356 | 0 | } |
357 | | |
358 | | void |
359 | | ws_manuf_dump(FILE *fp) |
360 | 0 | { |
361 | 0 | ws_manuf_iter_t iter; |
362 | 0 | struct ws_manuf item; |
363 | 0 | char strbuf[64]; |
364 | |
|
365 | 0 | ws_manuf_iter_init(&iter); |
366 | |
|
367 | 0 | while (ws_manuf_iter_next(&iter, &item)) { |
368 | 0 | fprintf(fp, "%-17s\t%-12s\t%s\n", |
369 | 0 | ws_manuf_block_str(strbuf, sizeof(strbuf), &item), |
370 | 0 | item.short_name, |
371 | 0 | item.long_name); |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | size_t |
376 | | ws_manuf_count(void) |
377 | 0 | { |
378 | 0 | return G_N_ELEMENTS(global_manuf_oui24_table) + |
379 | 0 | G_N_ELEMENTS(global_manuf_oui28_table) + |
380 | 0 | G_N_ELEMENTS(global_manuf_oui36_table); |
381 | 0 | } |