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

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

Remove most use of packet_t from NIC drivers.

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