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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 84239b1 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • 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/**
389 * Check if the frame passes through the receive control.
390 *
391 * @param rxc
392 * @param frame The probed frame
393 *
394 * @return True if the frame passes, false if it does not
395 */
396bool nic_rxc_check(const nic_rxc_t *rxc, const void *data, size_t size,
397 nic_frame_type_t *frame_type)
398{
399 assert(frame_type != NULL);
400 uint8_t *dest_addr = (uint8_t *) data;
401 uint8_t *src_addr = dest_addr + ETH_ADDR;
402
403 if (size < 2 * ETH_ADDR)
404 return false;
405
406 if (dest_addr[0] & 1) {
407 /* Multicast or broadcast */
408 if (*(uint32_t *) dest_addr == 0xFFFFFFFF &&
409 *(uint16_t *) (dest_addr + 4) == 0xFFFF) {
410 /* It is broadcast */
411 *frame_type = NIC_FRAME_BROADCAST;
412 if (rxc->broadcast_mode == NIC_BROADCAST_BLOCKED)
413 return false;
414 } else {
415 *frame_type = NIC_FRAME_MULTICAST;
416 /* In promiscuous mode the multicast_exact should be set to true */
417 if (!rxc->multicast_exact) {
418 if (rxc->multicast_mode == NIC_MULTICAST_BLOCKED)
419 return false;
420 else {
421 if (!nic_addr_db_contains(&rxc->multicast_addrs,
422 dest_addr))
423 return false;
424 }
425 }
426 }
427 } else {
428 /* Unicast */
429 *frame_type = NIC_FRAME_UNICAST;
430 /* In promiscuous mode the unicast_exact should be set to true */
431 if (!rxc->unicast_exact) {
432 if (rxc->unicast_mode == NIC_UNICAST_BLOCKED)
433 return false;
434 else {
435 if (!nic_addr_db_contains(&rxc->unicast_addrs,
436 dest_addr))
437 return false;
438 }
439 }
440 }
441
442 /* Blocked source addresses */
443 if (rxc->block_sources) {
444 if (nic_addr_db_contains(&rxc->blocked_sources, src_addr))
445 return false;
446 }
447
448 /* VLAN filtering */
449 if (!rxc->vlan_exact && rxc->vlan_mask != NULL) {
450 vlan_header_t *vlan_header = (vlan_header_t *)
451 ((uint8_t *) data + 2 * ETH_ADDR);
452 if (vlan_header->tpid_upper == VLAN_TPID_UPPER &&
453 vlan_header->tpid_lower == VLAN_TPID_LOWER) {
454 int index = ((int) (vlan_header->vid_upper & 0xF) << 5) |
455 (vlan_header->vid_lower >> 3);
456 if (!(rxc->vlan_mask->bitmap[index] &
457 (1 << (vlan_header->vid_lower & 0x7))))
458 return false;
459 }
460 }
461
462 return true;
463}
464
465/**
466 * Set information about current HW filtering.
467 * 1 ... Only those frames we want to receive are passed through HW
468 * 0 ... The HW filtering is imperfect
469 * -1 ... Don't change the setting
470 * This function should be called only from the mode change event handler.
471 *
472 * @param rxc
473 * @param unicast_exact Unicast frames
474 * @param mcast_exact Multicast frames
475 * @param vlan_exact VLAN tags
476 */
477void nic_rxc_hw_filtering(nic_rxc_t *rxc,
478 int unicast_exact, int multicast_exact, int vlan_exact)
479{
480 if (unicast_exact >= 0)
481 rxc->unicast_exact = unicast_exact;
482 if (multicast_exact >= 0)
483 rxc->multicast_exact = multicast_exact;
484 if (vlan_exact >= 0)
485 rxc->vlan_exact = vlan_exact;
486}
487
488/** Polynomial used in multicast address hashing */
489#define CRC_MCAST_POLYNOMIAL 0x04c11db6
490
491/** Compute the standard hash from MAC
492 *
493 * Hashing MAC into 64 possible values and using the value as index to
494 * 64bit number.
495 *
496 * The code is copied from qemu-0.13's implementation of ne2000 and rt8139
497 * drivers, but according to documentation there it originates in FreeBSD.
498 *
499 * @param[in] addr The 6-byte MAC address to be hashed
500 *
501 * @return 64-bit number with only single bit set to 1
502 *
503 */
504static uint64_t multicast_hash(const uint8_t addr[6])
505{
506 uint32_t crc;
507 int carry, i, j;
508 uint8_t b;
509
510 crc = 0xffffffff;
511 for (i = 0; i < 6; i++) {
512 b = addr[i];
513 for (j = 0; j < 8; j++) {
514 carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
515 crc <<= 1;
516 b >>= 1;
517 if (carry)
518 crc = ((crc ^ CRC_MCAST_POLYNOMIAL) | carry);
519 }
520 }
521
522 uint64_t one64 = 1;
523 return one64 << (crc >> 26);
524}
525
526
527/**
528 * Computes hash for the address list based on standard multicast address
529 * hashing.
530 *
531 * @param address_list
532 * @param count
533 *
534 * @return Multicast hash
535 *
536 * @see multicast_hash
537 */
538uint64_t nic_rxc_mcast_hash(const nic_address_t *address_list, size_t count)
539{
540 size_t i;
541 uint64_t hash = 0;
542 for (i = 0; i < count; ++i) {
543 hash |= multicast_hash(address_list[i].address);
544 }
545 return hash;
546}
547
548static void nic_rxc_hash_addr(const uint8_t *address, void *arg)
549{
550 *((uint64_t *) arg) |= multicast_hash(address);
551}
552
553/**
554 * Computes hash for multicast addresses currently set up in the RX multicast
555 * filtering. For promiscuous mode returns all ones, for blocking all zeroes.
556 * Can be called only from the on_*_change handler.
557 *
558 * @param rxc
559 *
560 * @return Multicast hash
561 *
562 * @see multicast_hash
563 */
564uint64_t nic_rxc_multicast_get_hash(const nic_rxc_t *rxc)
565{
566 switch (rxc->multicast_mode) {
567 case NIC_MULTICAST_UNKNOWN:
568 case NIC_MULTICAST_BLOCKED:
569 return 0;
570 case NIC_MULTICAST_LIST:
571 break;
572 case NIC_MULTICAST_PROMISC:
573 return ~ (uint64_t) 0;
574 }
575 uint64_t hash;
576 nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_hash_addr, &hash);
577 return hash;
578}
579
580/** @}
581 */
Note: See TracBrowser for help on using the repository browser.