HelenOS sources
This source file includes following definitions.
- nic_rxc_init
- nic_rxc_clear
- nic_rxc_set_addr
- nic_rxc_add_addr
- nic_rxc_unicast_get_mode
- nic_rxc_unicast_set_mode
- nic_rxc_multicast_get_mode
- nic_rxc_multicast_set_mode
- nic_rxc_broadcast_get_mode
- nic_rxc_broadcast_set_mode
- nic_rxc_blocked_sources_get
- nic_rxc_blocked_sources_set
- nic_rxc_vlan_get_mask
- nic_rxc_vlan_set_mask
- nic_rxc_check
- nic_rxc_hw_filtering
- multicast_hash
- nic_rxc_mcast_hash
- nic_rxc_hash_addr
- nic_rxc_multicast_get_hash
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <mem.h>
#include <nic/nic.h>
#include "nic_rx_control.h"
errno_t nic_rxc_init(nic_rxc_t *rxc)
{
memset(rxc, 0, sizeof(nic_rxc_t));
errno_t rc;
rc = nic_addr_db_init(&rxc->blocked_sources, ETH_ADDR);
if (rc != EOK) {
return rc;
}
rc = nic_addr_db_init(&rxc->unicast_addrs, ETH_ADDR);
if (rc != EOK) {
return rc;
}
rc = nic_addr_db_init(&rxc->multicast_addrs, ETH_ADDR);
if (rc != EOK) {
return rc;
}
rxc->block_sources = false;
rxc->unicast_mode = NIC_UNICAST_DEFAULT;
rxc->multicast_mode = NIC_MULTICAST_BLOCKED;
rxc->broadcast_mode = NIC_BROADCAST_ACCEPTED;
rxc->unicast_exact = true;
rxc->multicast_exact = false;
rxc->vlan_exact = true;
return EOK;
}
errno_t nic_rxc_clear(nic_rxc_t *rxc)
{
nic_addr_db_destroy(&rxc->unicast_addrs);
nic_addr_db_destroy(&rxc->multicast_addrs);
nic_addr_db_destroy(&rxc->blocked_sources);
return nic_rxc_init(rxc);
}
errno_t nic_rxc_set_addr(nic_rxc_t *rxc, const nic_address_t *prev_addr,
const nic_address_t *curr_addr)
{
if (prev_addr != NULL) {
errno_t rc = nic_addr_db_remove(&rxc->unicast_addrs,
(const uint8_t *) &prev_addr->address);
if (rc != EOK)
return rc;
}
return nic_addr_db_insert(&rxc->unicast_addrs,
(const uint8_t *) &curr_addr->address);
}
typedef struct {
size_t max_count;
nic_address_t *address_list;
size_t address_count;
} nic_rxc_add_addr_t;
static void nic_rxc_add_addr(const uint8_t *addr, void *arg)
{
nic_rxc_add_addr_t *hs = (nic_rxc_add_addr_t *) arg;
if (hs->address_count < hs->max_count && hs->address_list != NULL) {
memcpy(&hs->address_list[hs->address_count].address, addr, ETH_ADDR);
}
hs->address_count++;
}
void nic_rxc_unicast_get_mode(const nic_rxc_t *rxc, nic_unicast_mode_t *mode,
size_t max_count, nic_address_t *address_list, size_t *address_count)
{
*mode = rxc->unicast_mode;
if (rxc->unicast_mode == NIC_UNICAST_LIST) {
nic_rxc_add_addr_t hs = {
.max_count = max_count,
.address_list = address_list,
.address_count = 0
};
nic_addr_db_foreach(&rxc->unicast_addrs, nic_rxc_add_addr, &hs);
if (address_count) {
*address_count = hs.address_count;
}
}
}
errno_t nic_rxc_unicast_set_mode(nic_rxc_t *rxc, nic_unicast_mode_t mode,
const nic_address_t *address_list, size_t address_count)
{
if (mode == NIC_UNICAST_LIST && address_list == NULL) {
return EINVAL;
} else if (mode != NIC_UNICAST_LIST && address_list != NULL) {
return EINVAL;
}
if (rxc->unicast_mode == NIC_UNICAST_LIST) {
nic_addr_db_clear(&rxc->unicast_addrs);
}
rxc->unicast_mode = mode;
size_t i;
for (i = 0; i < address_count; ++i) {
errno_t rc = nic_addr_db_insert(&rxc->unicast_addrs,
(const uint8_t *) &address_list[i].address);
if (rc == ENOMEM) {
return ENOMEM;
}
}
return EOK;
}
void nic_rxc_multicast_get_mode(const nic_rxc_t *rxc,
nic_multicast_mode_t *mode, size_t max_count, nic_address_t *address_list,
size_t *address_count)
{
*mode = rxc->multicast_mode;
if (rxc->multicast_mode == NIC_MULTICAST_LIST) {
nic_rxc_add_addr_t hs = {
.max_count = max_count,
.address_list = address_list,
.address_count = 0
};
nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_add_addr, &hs);
if (address_count) {
*address_count = hs.address_count;
}
}
}
errno_t nic_rxc_multicast_set_mode(nic_rxc_t *rxc, nic_multicast_mode_t mode,
const nic_address_t *address_list, size_t address_count)
{
if (mode == NIC_MULTICAST_LIST && address_list == NULL)
return EINVAL;
else if (mode != NIC_MULTICAST_LIST && address_list != NULL)
return EINVAL;
if (rxc->multicast_mode == NIC_MULTICAST_LIST)
nic_addr_db_clear(&rxc->multicast_addrs);
rxc->multicast_mode = mode;
size_t i;
for (i = 0; i < address_count; ++i) {
errno_t rc = nic_addr_db_insert(&rxc->multicast_addrs,
(const uint8_t *)&address_list[i].address);
if (rc == ENOMEM) {
return ENOMEM;
}
}
return EOK;
}
void nic_rxc_broadcast_get_mode(const nic_rxc_t *rxc, nic_broadcast_mode_t *mode)
{
*mode = rxc->broadcast_mode;
}
errno_t nic_rxc_broadcast_set_mode(nic_rxc_t *rxc, nic_broadcast_mode_t mode)
{
rxc->broadcast_mode = mode;
return EOK;
}
void nic_rxc_blocked_sources_get(const nic_rxc_t *rxc,
size_t max_count, nic_address_t *address_list, size_t *address_count)
{
nic_rxc_add_addr_t hs = {
.max_count = max_count,
.address_list = address_list,
.address_count = 0
};
nic_addr_db_foreach(&rxc->blocked_sources, nic_rxc_add_addr, &hs);
if (address_count) {
*address_count = hs.address_count;
}
}
errno_t nic_rxc_blocked_sources_set(nic_rxc_t *rxc,
const nic_address_t *address_list, size_t address_count)
{
assert((address_count == 0 && address_list == NULL) ||
(address_count != 0 && address_list != NULL));
nic_addr_db_clear(&rxc->blocked_sources);
rxc->block_sources = (address_count != 0);
size_t i;
for (i = 0; i < address_count; ++i) {
errno_t rc = nic_addr_db_insert(&rxc->blocked_sources,
(const uint8_t *) &address_list[i].address);
if (rc == ENOMEM) {
return ENOMEM;
}
}
return EOK;
}
errno_t nic_rxc_vlan_get_mask(const nic_rxc_t *rxc, nic_vlan_mask_t *mask)
{
if (rxc->vlan_mask == NULL) {
return ENOENT;
}
memcpy(mask, rxc->vlan_mask, sizeof (nic_vlan_mask_t));
return EOK;
}
errno_t nic_rxc_vlan_set_mask(nic_rxc_t *rxc, const nic_vlan_mask_t *mask)
{
if (mask == NULL) {
if (rxc->vlan_mask) {
free(rxc->vlan_mask);
}
rxc->vlan_mask = NULL;
return EOK;
}
if (!rxc->vlan_mask) {
rxc->vlan_mask = malloc(sizeof (nic_vlan_mask_t));
if (rxc->vlan_mask == NULL) {
return ENOMEM;
}
}
memcpy(rxc->vlan_mask, mask, sizeof (nic_vlan_mask_t));
return EOK;
}
bool nic_rxc_check(const nic_rxc_t *rxc, const void *data, size_t size,
nic_frame_type_t *frame_type)
{
assert(frame_type != NULL);
uint8_t *dest_addr = (uint8_t *) data;
uint8_t *src_addr = dest_addr + ETH_ADDR;
if (size < 2 * ETH_ADDR)
return false;
if (dest_addr[0] & 1) {
if (*(uint32_t *) dest_addr == 0xFFFFFFFF &&
*(uint16_t *) (dest_addr + 4) == 0xFFFF) {
*frame_type = NIC_FRAME_BROADCAST;
if (rxc->broadcast_mode == NIC_BROADCAST_BLOCKED)
return false;
} else {
*frame_type = NIC_FRAME_MULTICAST;
if (!rxc->multicast_exact) {
if (rxc->multicast_mode == NIC_MULTICAST_BLOCKED)
return false;
else {
if (!nic_addr_db_contains(&rxc->multicast_addrs,
dest_addr))
return false;
}
}
}
} else {
*frame_type = NIC_FRAME_UNICAST;
if (!rxc->unicast_exact) {
if (rxc->unicast_mode == NIC_UNICAST_BLOCKED)
return false;
else {
if (!nic_addr_db_contains(&rxc->unicast_addrs,
dest_addr))
return false;
}
}
}
if (rxc->block_sources) {
if (nic_addr_db_contains(&rxc->blocked_sources, src_addr))
return false;
}
if (!rxc->vlan_exact && rxc->vlan_mask != NULL) {
vlan_header_t *vlan_header = (vlan_header_t *)
((uint8_t *) data + 2 * ETH_ADDR);
if (vlan_header->tpid_upper == VLAN_TPID_UPPER &&
vlan_header->tpid_lower == VLAN_TPID_LOWER) {
int index = ((int) (vlan_header->vid_upper & 0xF) << 5) |
(vlan_header->vid_lower >> 3);
if (!(rxc->vlan_mask->bitmap[index] &
(1 << (vlan_header->vid_lower & 0x7))))
return false;
}
}
return true;
}
void nic_rxc_hw_filtering(nic_rxc_t *rxc,
int unicast_exact, int multicast_exact, int vlan_exact)
{
if (unicast_exact >= 0)
rxc->unicast_exact = unicast_exact;
if (multicast_exact >= 0)
rxc->multicast_exact = multicast_exact;
if (vlan_exact >= 0)
rxc->vlan_exact = vlan_exact;
}
#define CRC_MCAST_POLYNOMIAL 0x04c11db6
static uint64_t multicast_hash(const uint8_t addr[6])
{
uint32_t crc;
int carry, i, j;
uint8_t b;
crc = 0xffffffff;
for (i = 0; i < 6; i++) {
b = addr[i];
for (j = 0; j < 8; j++) {
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
crc <<= 1;
b >>= 1;
if (carry)
crc = ((crc ^ CRC_MCAST_POLYNOMIAL) | carry);
}
}
uint64_t one64 = 1;
return one64 << (crc >> 26);
}
uint64_t nic_rxc_mcast_hash(const nic_address_t *address_list, size_t count)
{
size_t i;
uint64_t hash = 0;
for (i = 0; i < count; ++i) {
hash |= multicast_hash(address_list[i].address);
}
return hash;
}
static void nic_rxc_hash_addr(const uint8_t *address, void *arg)
{
*((uint64_t *) arg) |= multicast_hash(address);
}
uint64_t nic_rxc_multicast_get_hash(const nic_rxc_t *rxc)
{
switch (rxc->multicast_mode) {
case NIC_MULTICAST_UNKNOWN:
case NIC_MULTICAST_BLOCKED:
return 0;
case NIC_MULTICAST_LIST:
break;
case NIC_MULTICAST_PROMISC:
return ~(uint64_t) 0;
}
uint64_t hash;
nic_addr_db_foreach(&rxc->multicast_addrs, nic_rxc_hash_addr, &hash);
return hash;
}
HelenOS homepage, sources at GitHub