source: mainline/uspace/lib/nic/src/nic_rx_control.c@ eec201d

Last change on this file since eec201d was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1/*
2 * Copyright (c) 2011 Radim Vansa
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @addtogroup libnic
31 * @{
32 */
33/**
34 * @file
35 * @brief Incoming packets (frames) filtering functions
36 */
37
38#include <assert.h>
39#include <stdlib.h>
40#include <errno.h>
41#include <mem.h>
42#include <nic/nic.h>
43#include "nic_rx_control.h"
44
45/**
46 * Initializes the receive control structure
47 *
48 * @param rxc
49 *
50 * @return EOK On success
51 * @return ENOMEM On not enough memory
52 * @return EINVAL Internal error, should not happen
53 */
54errno_t nic_rxc_init(nic_rxc_t *rxc)
55{
56 memset(rxc, 0, sizeof(nic_rxc_t));
57 errno_t rc;
58 rc = nic_addr_db_init(&rxc->blocked_sources, ETH_ADDR);
59 if (rc != EOK) {
60 return rc;
61 }
62 rc = nic_addr_db_init(&rxc->unicast_addrs, ETH_ADDR);
63 if (rc != EOK) {
64 return rc;
65 }
66 rc = nic_addr_db_init(&rxc->multicast_addrs, ETH_ADDR);
67 if (rc != EOK) {
68 return rc;
69 }
70 rxc->block_sources = false;
71 rxc->unicast_mode = NIC_UNICAST_DEFAULT;
72 rxc->multicast_mode = NIC_MULTICAST_BLOCKED;
73 rxc->broadcast_mode = NIC_BROADCAST_ACCEPTED;
74
75 /* Default NIC behavior */
76 rxc->unicast_exact = true;
77 rxc->multicast_exact = false;
78 rxc->vlan_exact = true;
79 return EOK;
80}
81
82/** Reinitialize the structure.
83 *
84 * @param filters
85 */
86errno_t nic_rxc_clear(nic_rxc_t *rxc)
87{
88 nic_addr_db_destroy(&rxc->unicast_addrs);
89 nic_addr_db_destroy(&rxc->multicast_addrs);
90 nic_addr_db_destroy(&rxc->blocked_sources);
91 return nic_rxc_init(rxc);
92}
93
94/** Set the NIC's address that should be used as the default address during
95 * the checks.
96 *
97 * @param rxc
98 * @param prev_addr Previously used default address. Can be NULL
99 * if this is the first call after filters' initialization.
100 * @param curr_addr The new default address.
101 *
102 * @return EOK On success
103 *
104 */
105errno_t nic_rxc_set_addr(nic_rxc_t *rxc, const nic_address_t *prev_addr,
106 const nic_address_t *curr_addr)
107{
108 if (prev_addr != NULL) {
109 errno_t rc = nic_addr_db_remove(&rxc->unicast_addrs,
110 (const uint8_t *) &prev_addr->address);
111 if (rc != EOK)
112 return rc;
113 }
114
115 return nic_addr_db_insert(&rxc->unicast_addrs,
116 (const uint8_t *) &curr_addr->address);
117}
118
119/* Helper structure */
120typedef struct {
121 size_t max_count;
122 nic_address_t *address_list;
123 size_t address_count;
124} nic_rxc_add_addr_t;
125
126/** Helper function */
127static void nic_rxc_add_addr(const uint8_t *addr, void *arg)
128{
129 nic_rxc_add_addr_t *hs = (nic_rxc_add_addr_t *) arg;
130 if (hs->address_count < hs->max_count && hs->address_list != NULL) {
131 memcpy(&hs->address_list[hs->address_count].address, addr, ETH_ADDR);
132 }
133 hs->address_count++;
134}
135
136/**
137 * Queries the current mode of unicast frames receiving.
138 *
139 * @param rxc
140 * @param mode The new unicast mode
141 * @param max_count Max number of addresses that can be written into the
142 * address_list.
143 * @param address_list List of MAC addresses or NULL.
144 * @param address_count Number of accepted addresses (can be > max_count)
145 */
146void nic_rxc_unicast_get_mode(const nic_rxc_t *rxc, nic_unicast_mode_t *mode,
147 size_t max_count, nic_address_t *address_list, size_t *address_count)
148{
149 *mode = rxc->unicast_mode;
150 if (rxc->unicast_mode == NIC_UNICAST_LIST) {
151 nic_rxc_add_addr_t hs = {
152 .max_count = max_count,
153 .address_list = address_list,
154 .address_count = 0
155 };
156 nic_addr_db_foreach(&rxc->unicast_addrs, nic_rxc_add_addr, &hs);
157 if (address_count) {
158 *address_count = hs.address_count;
159 }
160 }
161}
162
163/**
164 * Sets the current mode of unicast frames receiving.
165 *
166 * @param rxc
167 * @param mode The current unicast mode
168 * @param address_list List of MAC addresses or NULL.
169 * @param address_count Number of addresses in the list
170 *
171 * @return EOK On success
172 * @return EINVAL If any of the MAC addresses is not a unicast address.
173 * @return ENOMEM If there was not enough memory
174 */
175errno_t nic_rxc_unicast_set_mode(nic_rxc_t *rxc, nic_unicast_mode_t mode,
176 const nic_address_t *address_list, size_t address_count)
177{
178 if (mode == NIC_UNICAST_LIST && address_list == NULL) {
179 return EINVAL;
180 } else if (mode != NIC_UNICAST_LIST && address_list != NULL) {
181 return EINVAL;
182 }
183
184 if (rxc->unicast_mode == NIC_UNICAST_LIST) {
185 nic_addr_db_clear(&rxc->unicast_addrs);
186 }
187 rxc->unicast_mode = mode;
188 size_t i;
189 for (i = 0; i < address_count; ++i) {
190 errno_t rc = nic_addr_db_insert(&rxc->unicast_addrs,
191 (const uint8_t *) &address_list[i].address);
192 if (rc == ENOMEM) {
193 return ENOMEM;
194 }
195 }
196 return EOK;
197}
198
199/**
200 * Queries the current mode of multicast frames receiving.
201 *
202 * @param rxc
203 * @param mode The current multicast mode
204 * @param max_count Max number of addresses that can be written into the
205 * address_list.
206 * @param address_list List of MAC addresses or NULL.
207 * @param address_count Number of accepted addresses (can be > max_count)
208 */
209void nic_rxc_multicast_get_mode(const nic_rxc_t *rxc,
210 nic_multicast_mode_t *mode, size_t max_count, nic_address_t *address_list,
211 size_t *address_count)
212{
213 *mode = rxc->multicast_mode;
214 if (rxc->multicast_mode == NIC_MULTICAST_LIST) {
215 nic_rxc_add_addr_t hs = {
216 .max_count = max_count,
217 .address_list = address_list,
218 .address_count = 0
219 };
220 nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_add_addr, &hs);
221 if (address_count) {
222 *address_count = hs.address_count;
223 }
224 }
225}
226
227/**
228 * Sets the current mode of multicast frames receiving.
229 *
230 * @param rxc
231 * @param mode The new multicast mode
232 * @param address_list List of MAC addresses or NULL.
233 * @param address_count Number of addresses in the list
234 *
235 * @return EOK On success
236 * @return EINVAL If any of the MAC addresses is not a multicast address.
237 * @return ENOMEM If there was not enough memory
238 */
239errno_t nic_rxc_multicast_set_mode(nic_rxc_t *rxc, nic_multicast_mode_t mode,
240 const nic_address_t *address_list, size_t address_count)
241{
242 if (mode == NIC_MULTICAST_LIST && address_list == NULL)
243 return EINVAL;
244 else if (mode != NIC_MULTICAST_LIST && address_list != NULL)
245 return EINVAL;
246
247 if (rxc->multicast_mode == NIC_MULTICAST_LIST)
248 nic_addr_db_clear(&rxc->multicast_addrs);
249
250 rxc->multicast_mode = mode;
251 size_t i;
252 for (i = 0; i < address_count; ++i) {
253 errno_t rc = nic_addr_db_insert(&rxc->multicast_addrs,
254 (const uint8_t *)&address_list[i].address);
255 if (rc == ENOMEM) {
256 return ENOMEM;
257 }
258 }
259 return EOK;
260}
261
262/**
263 * Queries the current mode of broadcast frames receiving.
264 *
265 * @param rxc
266 * @param mode The new broadcast mode
267 */
268void nic_rxc_broadcast_get_mode(const nic_rxc_t *rxc, nic_broadcast_mode_t *mode)
269{
270 *mode = rxc->broadcast_mode;
271}
272
273/**
274 * Sets the current mode of broadcast frames receiving.
275 *
276 * @param rxc
277 * @param mode The new broadcast mode
278 *
279 * @return EOK On success
280 */
281errno_t nic_rxc_broadcast_set_mode(nic_rxc_t *rxc, nic_broadcast_mode_t mode)
282{
283 rxc->broadcast_mode = mode;
284 return EOK;
285}
286
287/**
288 * Queries the current blocked source addresses.
289 *
290 * @param rxc
291 * @param max_count Max number of addresses that can be written into the
292 * address_list.
293 * @param address_list List of MAC addresses or NULL.
294 * @param address_count Number of blocked addresses (can be > max_count)
295 */
296void nic_rxc_blocked_sources_get(const nic_rxc_t *rxc,
297 size_t max_count, nic_address_t *address_list, size_t *address_count)
298{
299 nic_rxc_add_addr_t hs = {
300 .max_count = max_count,
301 .address_list = address_list,
302 .address_count = 0
303 };
304 nic_addr_db_foreach(&rxc->blocked_sources, nic_rxc_add_addr, &hs);
305 if (address_count) {
306 *address_count = hs.address_count;
307 }
308}
309
310/**
311 * Clears the currently blocked addresses and sets the addresses contained in
312 * the list as the set of blocked source addresses (no frame with this source
313 * address will be received). Duplicated addresses are ignored.
314 *
315 * @param rxc
316 * @param address_list List of the blocked addresses. Can be NULL.
317 * @param address_count Number of addresses in the list
318 *
319 * @return EOK On success
320 * @return ENOMEM If there was not enough memory
321 */
322errno_t nic_rxc_blocked_sources_set(nic_rxc_t *rxc,
323 const nic_address_t *address_list, size_t address_count)
324{
325 assert((address_count == 0 && address_list == NULL) ||
326 (address_count != 0 && address_list != NULL));
327
328 nic_addr_db_clear(&rxc->blocked_sources);
329 rxc->block_sources = (address_count != 0);
330 size_t i;
331 for (i = 0; i < address_count; ++i) {
332 errno_t rc = nic_addr_db_insert(&rxc->blocked_sources,
333 (const uint8_t *) &address_list[i].address);
334 if (rc == ENOMEM) {
335 return ENOMEM;
336 }
337 }
338 return EOK;
339}
340
341/**
342 * Query mask used for filtering according to the VLAN tags.
343 *
344 * @param rxc
345 * @param mask Must be 512 bytes long
346 *
347 * @return EOK
348 * @return ENOENT
349 */
350errno_t nic_rxc_vlan_get_mask(const nic_rxc_t *rxc, nic_vlan_mask_t *mask)
351{
352 if (rxc->vlan_mask == NULL) {
353 return ENOENT;
354 }
355 memcpy(mask, rxc->vlan_mask, sizeof (nic_vlan_mask_t));
356 return EOK;
357}
358
359/**
360 * Set mask for filtering according to the VLAN tags.
361 *
362 * @param rxc
363 * @param mask Must be 512 bytes long
364 *
365 * @return EOK
366 * @return ENOMEM
367 */
368errno_t nic_rxc_vlan_set_mask(nic_rxc_t *rxc, const nic_vlan_mask_t *mask)
369{
370 if (mask == NULL) {
371 if (rxc->vlan_mask) {
372 free(rxc->vlan_mask);
373 }
374 rxc->vlan_mask = NULL;
375 return EOK;
376 }
377 if (!rxc->vlan_mask) {
378 rxc->vlan_mask = malloc(sizeof (nic_vlan_mask_t));
379 if (rxc->vlan_mask == NULL) {
380 return ENOMEM;
381 }
382 }
383 memcpy(rxc->vlan_mask, mask, sizeof (nic_vlan_mask_t));
384 return EOK;
385}
386
387/**
388 * Check if the frame passes through the receive control.
389 *
390 * @param rxc
391 * @param frame The probed frame
392 *
393 * @return True if the frame passes, false if it does not
394 */
395bool nic_rxc_check(const nic_rxc_t *rxc, const void *data, size_t size,
396 nic_frame_type_t *frame_type)
397{
398 assert(frame_type != NULL);
399 uint8_t *dest_addr = (uint8_t *) data;
400 uint8_t *src_addr = dest_addr + ETH_ADDR;
401
402 if (size < 2 * ETH_ADDR)
403 return false;
404
405 if (dest_addr[0] & 1) {
406 /* Multicast or broadcast */
407 if (*(uint32_t *) dest_addr == 0xFFFFFFFF &&
408 *(uint16_t *) (dest_addr + 4) == 0xFFFF) {
409 /* It is broadcast */
410 *frame_type = NIC_FRAME_BROADCAST;
411 if (rxc->broadcast_mode == NIC_BROADCAST_BLOCKED)
412 return false;
413 } else {
414 *frame_type = NIC_FRAME_MULTICAST;
415 /* In promiscuous mode the multicast_exact should be set to true */
416 if (!rxc->multicast_exact) {
417 if (rxc->multicast_mode == NIC_MULTICAST_BLOCKED)
418 return false;
419 else {
420 if (!nic_addr_db_contains(&rxc->multicast_addrs,
421 dest_addr))
422 return false;
423 }
424 }
425 }
426 } else {
427 /* Unicast */
428 *frame_type = NIC_FRAME_UNICAST;
429 /* In promiscuous mode the unicast_exact should be set to true */
430 if (!rxc->unicast_exact) {
431 if (rxc->unicast_mode == NIC_UNICAST_BLOCKED)
432 return false;
433 else {
434 if (!nic_addr_db_contains(&rxc->unicast_addrs,
435 dest_addr))
436 return false;
437 }
438 }
439 }
440
441 /* Blocked source addresses */
442 if (rxc->block_sources) {
443 if (nic_addr_db_contains(&rxc->blocked_sources, src_addr))
444 return false;
445 }
446
447 /* VLAN filtering */
448 if (!rxc->vlan_exact && rxc->vlan_mask != NULL) {
449 vlan_header_t *vlan_header = (vlan_header_t *)
450 ((uint8_t *) data + 2 * ETH_ADDR);
451 if (vlan_header->tpid_upper == VLAN_TPID_UPPER &&
452 vlan_header->tpid_lower == VLAN_TPID_LOWER) {
453 int index = ((int) (vlan_header->vid_upper & 0xF) << 5) |
454 (vlan_header->vid_lower >> 3);
455 if (!(rxc->vlan_mask->bitmap[index] &
456 (1 << (vlan_header->vid_lower & 0x7))))
457 return false;
458 }
459 }
460
461 return true;
462}
463
464/**
465 * Set information about current HW filtering.
466 * 1 ... Only those frames we want to receive are passed through HW
467 * 0 ... The HW filtering is imperfect
468 * -1 ... Don't change the setting
469 * This function should be called only from the mode change event handler.
470 *
471 * @param rxc
472 * @param unicast_exact Unicast frames
473 * @param mcast_exact Multicast frames
474 * @param vlan_exact VLAN tags
475 */
476void nic_rxc_hw_filtering(nic_rxc_t *rxc,
477 int unicast_exact, int multicast_exact, int vlan_exact)
478{
479 if (unicast_exact >= 0)
480 rxc->unicast_exact = unicast_exact;
481 if (multicast_exact >= 0)
482 rxc->multicast_exact = multicast_exact;
483 if (vlan_exact >= 0)
484 rxc->vlan_exact = vlan_exact;
485}
486
487/** Polynomial used in multicast address hashing */
488#define CRC_MCAST_POLYNOMIAL 0x04c11db6
489
490/** Compute the standard hash from MAC
491 *
492 * Hashing MAC into 64 possible values and using the value as index to
493 * 64bit number.
494 *
495 * The code is copied from qemu-0.13's implementation of ne2000 and rt8139
496 * drivers, but according to documentation there it originates in FreeBSD.
497 *
498 * @param[in] addr The 6-byte MAC address to be hashed
499 *
500 * @return 64-bit number with only single bit set to 1
501 *
502 */
503static uint64_t multicast_hash(const uint8_t addr[6])
504{
505 uint32_t crc;
506 int carry, i, j;
507 uint8_t b;
508
509 crc = 0xffffffff;
510 for (i = 0; i < 6; i++) {
511 b = addr[i];
512 for (j = 0; j < 8; j++) {
513 carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
514 crc <<= 1;
515 b >>= 1;
516 if (carry)
517 crc = ((crc ^ CRC_MCAST_POLYNOMIAL) | carry);
518 }
519 }
520
521 uint64_t one64 = 1;
522 return one64 << (crc >> 26);
523}
524
525/**
526 * Computes hash for the address list based on standard multicast address
527 * hashing.
528 *
529 * @param address_list
530 * @param count
531 *
532 * @return Multicast hash
533 *
534 * @see multicast_hash
535 */
536uint64_t nic_rxc_mcast_hash(const nic_address_t *address_list, size_t count)
537{
538 size_t i;
539 uint64_t hash = 0;
540 for (i = 0; i < count; ++i) {
541 hash |= multicast_hash(address_list[i].address);
542 }
543 return hash;
544}
545
546static void nic_rxc_hash_addr(const uint8_t *address, void *arg)
547{
548 *((uint64_t *) arg) |= multicast_hash(address);
549}
550
551/**
552 * Computes hash for multicast addresses currently set up in the RX multicast
553 * filtering. For promiscuous mode returns all ones, for blocking all zeroes.
554 * Can be called only from the on_*_change handler.
555 *
556 * @param rxc
557 *
558 * @return Multicast hash
559 *
560 * @see multicast_hash
561 */
562uint64_t nic_rxc_multicast_get_hash(const nic_rxc_t *rxc)
563{
564 switch (rxc->multicast_mode) {
565 case NIC_MULTICAST_UNKNOWN:
566 case NIC_MULTICAST_BLOCKED:
567 return 0;
568 case NIC_MULTICAST_LIST:
569 break;
570 case NIC_MULTICAST_PROMISC:
571 return ~(uint64_t) 0;
572 }
573 uint64_t hash;
574 nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_hash_addr, &hash);
575 return hash;
576}
577
578/** @}
579 */
Note: See TracBrowser for help on using the repository browser.