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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cf9cb36 was cf9cb36, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Decouple libnic from libnet.

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