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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 612af1a0 was 00d7e1b, checked in by Martin Decky <martin@…>, 14 years ago

networking improvements

  • start the networking stack from init
  • add loopback network interface driver (cherrypicked and sanitized from lp:~helenos-nicf/helenos/nicf)
  • add libnic and various small pieces from lp:~helenos-nicf/helenos/nicf
  • fix client side of NIC_GET_ADDRESS
  • net binary overhaul

Note: "ping 127.0.0.1" works, but the first three pings timeout for some reason

  • 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 packet 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 packet_t *packet,
399 nic_frame_type_t *frame_type)
400{
401 assert(frame_type != NULL);
402 uint8_t *dest_addr = (uint8_t *) packet + packet->data_start;
403 uint8_t *src_addr = dest_addr + ETH_ADDR;
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 /* Blocked source addresses */
441 if (rxc->block_sources) {
442 if (nic_addr_db_contains(&rxc->blocked_sources, src_addr))
443 return false;
444 }
445 /* VLAN filtering */
446 if (!rxc->vlan_exact && rxc->vlan_mask != NULL) {
447 vlan_header_t *vlan_header = (vlan_header_t *)
448 ((uint8_t *) packet + packet->data_start + 2 * ETH_ADDR);
449 if (vlan_header->tpid_upper == VLAN_TPID_UPPER &&
450 vlan_header->tpid_lower == VLAN_TPID_LOWER) {
451 int index = ((int) (vlan_header->vid_upper & 0xF) << 5) |
452 (vlan_header->vid_lower >> 3);
453 if (!(rxc->vlan_mask->bitmap[index] &
454 (1 << (vlan_header->vid_lower & 0x7))))
455 return false;
456 }
457 }
458
459 return true;
460}
461
462/**
463 * Set information about current HW filtering.
464 * 1 ... Only those frames we want to receive are passed through HW
465 * 0 ... The HW filtering is imperfect
466 * -1 ... Don't change the setting
467 * This function should be called only from the mode change event handler.
468 *
469 * @param rxc
470 * @param unicast_exact Unicast frames
471 * @param mcast_exact Multicast frames
472 * @param vlan_exact VLAN tags
473 */
474void nic_rxc_hw_filtering(nic_rxc_t *rxc,
475 int unicast_exact, int multicast_exact, int vlan_exact)
476{
477 if (unicast_exact >= 0)
478 rxc->unicast_exact = unicast_exact;
479 if (multicast_exact >= 0)
480 rxc->multicast_exact = multicast_exact;
481 if (vlan_exact >= 0)
482 rxc->vlan_exact = vlan_exact;
483}
484
485/**
486 * Computes hash for the address list based on standard multicast address
487 * hashing.
488 *
489 * @param address_list
490 * @param count
491 *
492 * @return Multicast hash
493 *
494 * @see multicast_hash
495 */
496uint64_t nic_rxc_mcast_hash(const nic_address_t *address_list, size_t count)
497{
498 size_t i;
499 uint64_t hash = 0;
500 for (i = 0; i < count; ++i) {
501 hash |= multicast_hash(address_list[i].address);
502 }
503 return hash;
504}
505
506static void nic_rxc_hash_addr(const uint8_t *address, void *arg)
507{
508 *((uint64_t *) arg) |= multicast_hash(address);
509}
510
511/**
512 * Computes hash for multicast addresses currently set up in the RX multicast
513 * filtering. For promiscuous mode returns all ones, for blocking all zeroes.
514 * Can be called only from the on_*_change handler.
515 *
516 * @param rxc
517 *
518 * @return Multicast hash
519 *
520 * @see multicast_hash
521 */
522uint64_t nic_rxc_multicast_get_hash(const nic_rxc_t *rxc)
523{
524 switch (rxc->multicast_mode) {
525 case NIC_MULTICAST_UNKNOWN:
526 case NIC_MULTICAST_BLOCKED:
527 return 0;
528 case NIC_MULTICAST_LIST:
529 break;
530 case NIC_MULTICAST_PROMISC:
531 return ~ (uint64_t) 0;
532 }
533 uint64_t hash;
534 nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_hash_addr, &hash);
535 return hash;
536}
537
538/** @}
539 */
Note: See TracBrowser for help on using the repository browser.