1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15287 from miri64/ethernet/enh/join-leave-mcast-group

gnrc_netif: add capability to join or leave link layer multicast groups
This commit is contained in:
benpicco 2020-10-29 16:03:32 +01:00 committed by GitHub
commit 9e4dd8e451
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 453 additions and 9 deletions

View File

@ -693,12 +693,36 @@ static inline int gnrc_netif_ndp_addr_len_from_l2ao(gnrc_netif_t *netif,
assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR);
return l2util_ndp_addr_len_from_l2ao(netif->device_type, opt);
}
/**
* @brief Converts an IPv6 multicast address to a multicast address
* of the respective link layer.
*
* @pre There is enough allocated space in @p l2_group for an address for a
* device of type @p dev_type (e.g. 6 bytes for an ethernet address).
*
* @param[in] dev_type The network interface @p l2_addr should be generated
* for.
* @param[in] ipv6_group An IPv6 multicast address.
* @param[out] l2_group A link layer multicast address
*
* @return Length of @p l2_group in bytes
* @return `-ENOTSUP` if link layer does not support multicast.
*/
static inline int gnrc_netif_ipv6_group_to_l2_group(gnrc_netif_t *netif,
const ipv6_addr_t *ipv6_group,
uint8_t *l2_group)
{
return l2util_ipv6_group_to_l2_group(netif->device_type, ipv6_group,
l2_group);
}
#else /* IS_USED(MODULE_GNRC_NETIF_IPV6) || defined(DOXYGEN) */
#define gnrc_netif_ipv6_init_mtu(netif) (void)netif
#define gnrc_netif_ipv6_iid_from_addr(netif, addr, addr_len, iid) (-ENOTSUP)
#define gnrc_netif_ipv6_iid_to_addr(netif, iid, addr) (-ENOTSUP)
#define gnrc_netif_ndp_addr_len_from_l2ao(netif, opt) (-ENOTSUP)
#define gnrc_netif_ipv6_get_iid(netif, iid) (-ENOTSUP)
#define gnrc_netif_ipv6_group_to_l2_group(netif, ipv6_group, l2_group) (-ENOTSUP)
#endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) || defined(DOXYGEN) */
/** @} */

View File

@ -134,6 +134,25 @@ int l2util_ndp_addr_len_from_l2ao(int dev_type,
const ndp_opt_t *opt);
/**
* @brief Converts an IPv6 multicast address to a multicast address
* of the respective link layer.
*
* @pre There is enough allocated space in @p l2_group for an address for a
* device of type @p dev_type (e.g. 6 bytes for an ethernet address).
*
* @param[in] dev_type The network device type of the device @p l2_addr
* should be generated for.
* @param[in] ipv6_group An IPv6 multicast address.
* @param[out] l2_group A link layer multicast address
*
* @return Length of @p l2_group in bytes
* @return `-ENOTSUP` if link layer does not support multicast.
*/
int l2util_ipv6_group_to_l2_group(int dev_type,
const ipv6_addr_t *ipv6_group,
uint8_t *l2_group);
#ifdef __cplusplus
}
#endif

View File

@ -747,6 +747,21 @@ typedef enum {
*/
NETOPT_RSSI,
/**
* @brief (array of byte array) get link layer multicast groups as array
* of byte arrays (length of each byte array corresponds to the
* length of @ref NETOPT_ADDRESS) or join a link layer multicast
* group as byte array on an interface
*
* When getting the option you can pass an array of byte arrays of any
* length greater than 0 to the getter. The array will be filled up to to
* its maximum and the remaining addresses on the interface will be ignored
*/
NETOPT_L2_GROUP,
/**
* @brief (array of byte arrays) Leave an link layer multicast group
*/
NETOPT_L2_GROUP_LEAVE,
/**
* @brief maximum number of options defined here.
*

View File

@ -122,6 +122,8 @@ static const char *_netopt_strmap[] = {
[NETOPT_NUM_GATEWAYS] = "NETOPT_NUM_GATEWAYS",
[NETOPT_LINK_CHECK] = "NETOPT_LINK_CHECK",
[NETOPT_RSSI] = "NETOPT_RSSI",
[NETOPT_L2_GROUP] = "NETOPT_L2_GROUP",
[NETOPT_L2_GROUP_LEAVE] = "NETOPT_L2_GROUP_LEAVE",
[NETOPT_NUMOF] = "NETOPT_NUMOF",
};

View File

@ -62,19 +62,19 @@ static inline void _addr_set_broadcast(uint8_t *dst)
memset(dst, 0xff, ETHERNET_ADDR_LEN);
}
static inline void _addr_set_multicast(uint8_t *dst, gnrc_pktsnip_t *payload)
static inline void _addr_set_multicast(gnrc_netif_t *netif, uint8_t *dst,
gnrc_pktsnip_t *payload)
{
switch (payload->type) {
#ifdef MODULE_GNRC_IPV6
case GNRC_NETTYPE_IPV6:
/* https://tools.ietf.org/html/rfc2464#section-7 */
dst[0] = 0x33;
dst[1] = 0x33;
case GNRC_NETTYPE_IPV6: {
ipv6_hdr_t *ipv6 = payload->data;
memcpy(dst + 2, ipv6->dst.u8 + 12, 4);
gnrc_netif_ipv6_group_to_l2_group(netif, &ipv6->dst, dst);
break;
}
#endif
default:
(void)netif;
_addr_set_broadcast(dst);
break;
}
@ -128,7 +128,7 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
"are not yet supported\n");
return -ENOTSUP;
}
_addr_set_multicast(hdr.dst, payload);
_addr_set_multicast(netif, hdr.dst, payload);
}
else if (netif_hdr->dst_l2addr_len == ETHERNET_ADDR_LEN) {
memcpy(hdr.dst, gnrc_netif_hdr_get_dst_addr(netif_hdr),

View File

@ -717,12 +717,44 @@ gnrc_netif_t *gnrc_netif_get_by_prefix(const ipv6_addr_t *prefix)
return best_netif;
}
static int _netif_ops_set_helper(gnrc_netif_t *netif, netopt_t opt,
void *data, uint16_t data_len)
{
gnrc_netapi_opt_t netapi_opt = {
.opt = opt,
.data = data,
.data_len = data_len,
};
return netif->ops->set(netif, &netapi_opt);
}
int gnrc_netif_ipv6_group_join_internal(gnrc_netif_t *netif,
const ipv6_addr_t *addr)
{
uint8_t l2_group_data[GNRC_NETIF_L2ADDR_MAXLEN];
unsigned idx = UINT_MAX;
int l2_group_len;
/* can be called out of lock */
l2_group_len = gnrc_netif_ipv6_group_to_l2_group(netif, addr,
l2_group_data);
gnrc_netif_acquire(netif);
if (l2_group_len > 0) {
int res = _netif_ops_set_helper(netif, NETOPT_L2_GROUP,
l2_group_data, (uint16_t)l2_group_len);
/* link layer does not support multicast, but we can still use
* broadcast */
if ((res != -ENOTSUP) && (res < 0)) {
gnrc_netif_release(netif);
return res;
}
}
/* link layer does not support multicast, but we can still use
* broadcast */
else if (l2_group_len != -ENOTSUP) {
gnrc_netif_release(netif);
return l2_group_len;
}
for (unsigned i = 0; i < GNRC_NETIF_IPV6_GROUPS_NUMOF; i++) {
if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) {
gnrc_netif_release(netif);
@ -748,11 +780,56 @@ int gnrc_netif_ipv6_group_join_internal(gnrc_netif_t *netif,
void gnrc_netif_ipv6_group_leave_internal(gnrc_netif_t *netif,
const ipv6_addr_t *addr)
{
int idx;
uint8_t l2_group_data[GNRC_NETIF_L2ADDR_MAXLEN];
int idx = -1, l2_group_len;
/* IPv6 addresses that correspond to the same L2 address */
unsigned l2_groups = 0;
assert((netif != NULL) && (addr != NULL));
/* can be called out of lock */
l2_group_len = gnrc_netif_ipv6_group_to_l2_group(netif, addr,
l2_group_data);
/* link layer does not support multicast, but might still have used
* broadcast */
if ((l2_group_len < 0) && (l2_group_len != -ENOTSUP)) {
return;
}
gnrc_netif_acquire(netif);
idx = _group_idx(netif, addr);
for (unsigned i = 0; i < GNRC_NETIF_IPV6_GROUPS_NUMOF; i++) {
if (l2_group_len > 0) {
uint8_t tmp[GNRC_NETIF_L2ADDR_MAXLEN];
if (!ipv6_addr_is_unspecified(&netif->ipv6.groups[i]) &&
(gnrc_netif_ipv6_group_to_l2_group(netif,
&netif->ipv6.groups[i],
tmp) == l2_group_len)) {
if (memcmp(tmp, l2_group_data, l2_group_len) == 0) {
l2_groups++;
}
}
}
if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) {
idx = i;
}
}
if (idx < 0) {
gnrc_netif_release(netif);
return;
}
/* we need to have found at least one corresponding group for the IPv6
* group we want to leave when link layer supports multicast */
assert((l2_group_len == -ENOTSUP) || (l2_groups > 0));
/* we only found exactly IPv6 multicast address that corresponds to
* `l2_group_data`, so we can remove it, if there would be more, we need
* to stay in the group */
if (l2_groups == 1) {
int res = _netif_ops_set_helper(netif, NETOPT_L2_GROUP_LEAVE,
l2_group_data, (uint16_t)l2_group_len);
/* link layer does not support multicast, but might still have used
* broadcast */
if ((res != -ENOTSUP) && (res < 0)) {
DEBUG("gnrc_netif: error leaving link layer group\n");
}
}
if (idx >= 0) {
ipv6_addr_set_unspecified(&netif->ipv6.groups[idx]);
/* TODO:

View File

@ -238,5 +238,27 @@ int l2util_ndp_addr_len_from_l2ao(int dev_type,
return -ENOTSUP;
}
int l2util_ipv6_group_to_l2_group(int dev_type,
const ipv6_addr_t *ipv6_group,
uint8_t *l2_group)
{
switch (dev_type) {
#if IS_USED(MODULE_NETDEV_ETH)
case NETDEV_TYPE_ETHERNET:
/* see https://tools.ietf.org/html/rfc2464#section-7 */
l2_group[0] = 0x33;
l2_group[1] = 0x33;
l2_group[2] = ipv6_group->u8[12];
l2_group[3] = ipv6_group->u8[13];
l2_group[4] = ipv6_group->u8[14];
l2_group[5] = ipv6_group->u8[15];
return sizeof(eui48_t);
#endif
default:
(void)ipv6_group;
(void)l2_group;
return -ENOTSUP;
}
}
/** @} */

View File

@ -21,6 +21,7 @@
#include <errno.h>
#include <stdio.h>
#include "bitfield.h"
#include "common.h"
#include "embUnit.h"
#include "embUnit/embUnit.h"
@ -41,6 +42,7 @@
#define ETHERNET_STACKSIZE (THREAD_STACKSIZE_MAIN)
#define IEEE802154_STACKSIZE (THREAD_STACKSIZE_MAIN)
#define ETHERNET_GROUPS_MAX 8U
static gnrc_netif_t ethernet_netif;
static gnrc_netif_t ieee802154_netif;
@ -50,6 +52,9 @@ static char ieee802154_netif_stack[ETHERNET_STACKSIZE];
static char netifs_stack[DEFAULT_DEVS_NUMOF][THREAD_STACKSIZE_DEFAULT];
static bool init_called = false;
static uint8_t ethernet_groups[ETHERNET_GROUPS_MAX][ETHERNET_ADDR_LEN];
static BITFIELD(ethernet_groups_set, ETHERNET_GROUPS_MAX);
static inline void _test_init(gnrc_netif_t *netif);
static inline int _mock_netif_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt);
static inline gnrc_pktsnip_t *_mock_netif_recv(gnrc_netif_t * netif);
@ -62,6 +67,11 @@ static int _set_netdev_address_long(netdev_t *dev, const void *value,
static int _get_netdev_src_len(netdev_t *dev, void *value, size_t max_len);
static int _set_netdev_src_len(netdev_t *dev, const void *value,
size_t value_len);
static int _get_netdev_l2_group(netdev_t *dev, void *value, size_t max_len);
static int _set_netdev_l2_group(netdev_t *dev, const void *value,
size_t value_len);
static int _set_netdev_l2_group_leave(netdev_t *dev, const void *value,
size_t value_len);
static const gnrc_netif_ops_t default_ops = {
.init = _test_init,
@ -76,6 +86,8 @@ static void _set_up(void)
{
msg_t msg;
/* reset ethernet groups */
memset(ethernet_groups_set, 0, sizeof(ethernet_groups_set));
memset(ethernet_netif.ipv6.addrs_flags, 0,
sizeof(ethernet_netif.ipv6.addrs_flags));
memset(ethernet_netif.ipv6.addrs, 0,
@ -915,6 +927,55 @@ static void test_netapi_get__ADDRESS_LONG(void)
&value, sizeof(value)));
}
static void test_netapi_set_get__L2_GROUP(void)
{
static const uint8_t exp_group1[] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };
static const uint8_t exp_group2[] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x02 };
uint8_t value[2][ETHERNET_ADDR_LEN];
TEST_ASSERT_EQUAL_INT(sizeof(exp_group1),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&exp_group1, sizeof(exp_group1)));
TEST_ASSERT_EQUAL_INT(sizeof(exp_group2),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&exp_group2, sizeof(exp_group2)));
TEST_ASSERT_EQUAL_INT(sizeof(value),
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&value, sizeof(value)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_group1, value[0], sizeof(exp_group1)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_group2, value[1], sizeof(exp_group2)));
TEST_ASSERT_EQUAL_INT(-ENOTSUP,
gnrc_netapi_get(ieee802154_netif.pid,
NETOPT_L2_GROUP, 0,
&value, sizeof(value)));
}
static void test_netapi_set__L2_GROUP_LEAVE(void)
{
static const uint8_t exp_group1[] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };
static const uint8_t exp_group2[] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x02 };
uint8_t value[2][ETHERNET_ADDR_LEN];
test_netapi_set_get__L2_GROUP();
TEST_ASSERT_EQUAL_INT(sizeof(exp_group1),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_L2_GROUP_LEAVE, 0,
&exp_group1, sizeof(exp_group1)));
/* exp_group1 was removed */
TEST_ASSERT_EQUAL_INT(sizeof(exp_group2),
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&value, sizeof(value)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_group2, value[0], sizeof(exp_group2)));
TEST_ASSERT_EQUAL_INT(-ENOTSUP,
gnrc_netapi_get(ieee802154_netif.pid,
NETOPT_L2_GROUP_LEAVE, 0,
&value, sizeof(value)));
}
static void test_netapi_set__HOP_LIMIT(void)
{
uint8_t value = 89;
@ -965,6 +1026,30 @@ static void test_netapi_set__IPV6_GROUP(void)
TEST_ASSERT(0 <= gnrc_netif_ipv6_group_idx(&netifs[0], &value));
}
static void test_netapi_set__IPV6_GROUP__ethernet(void)
{
ipv6_addr_t value = IPV6_ADDR_ALL_NODES_LINK_LOCAL;
uint8_t exp_ethernet[ETHERNET_ADDR_LEN];
uint8_t l2_value[1][ETHERNET_ADDR_LEN];
TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet),
gnrc_netif_ipv6_group_to_l2_group(&ethernet_netif,
&value,
exp_ethernet));
TEST_ASSERT(0 > gnrc_netif_ipv6_group_idx(&ethernet_netif, &value));
TEST_ASSERT_EQUAL_INT(sizeof(value),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_IPV6_GROUP, 0,
&value, sizeof(value)));
TEST_ASSERT(0 <= gnrc_netif_ipv6_group_idx(&ethernet_netif, &value));
TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet),
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_ethernet, l2_value[0],
sizeof(exp_ethernet)));
}
static void test_netapi_set__IPV6_GROUP_LEAVE(void)
{
ipv6_addr_t value = IPV6_ADDR_ALL_NODES_LINK_LOCAL;
@ -978,6 +1063,87 @@ static void test_netapi_set__IPV6_GROUP_LEAVE(void)
TEST_ASSERT(0 > gnrc_netif_ipv6_group_idx(&netifs[0], &value));
}
static void test_netapi_set__IPV6_GROUP_LEAVE__ethernet(void)
{
ipv6_addr_t value = IPV6_ADDR_ALL_NODES_LINK_LOCAL;
uint8_t l2_value[1][ETHERNET_ADDR_LEN];
/* no L2 group assigned */
TEST_ASSERT_EQUAL_INT(0,
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
/* join a group */
test_netapi_set__IPV6_GROUP__ethernet();
TEST_ASSERT(0 <= gnrc_netif_ipv6_group_idx(&ethernet_netif, &value));
TEST_ASSERT_EQUAL_INT(sizeof(value),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_IPV6_GROUP_LEAVE, 0,
&value, sizeof(value)));
TEST_ASSERT(0 > gnrc_netif_ipv6_group_idx(&ethernet_netif, &value));
/* no L2 group assigned */
TEST_ASSERT_EQUAL_INT(0,
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
}
static void test_netapi_set__IPV6_GROUP_LEAVE__ethernet_two_same(void)
{
ipv6_addr_t value1 = IPV6_ADDR_ALL_NODES_LINK_LOCAL;
ipv6_addr_t value2 = IPV6_ADDR_ALL_NODES_IF_LOCAL;
uint8_t exp_ethernet[ETHERNET_ADDR_LEN];
uint8_t l2_value[2][ETHERNET_ADDR_LEN];
/* no L2 group assigned */
TEST_ASSERT_EQUAL_INT(0,
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet),
gnrc_netif_ipv6_group_to_l2_group(&ethernet_netif,
&value1,
exp_ethernet));
/* join a group */
test_netapi_set__IPV6_GROUP__ethernet();
TEST_ASSERT(0 <= gnrc_netif_ipv6_group_idx(&ethernet_netif, &value1));
/* join another IPv6 group with same suffix */
TEST_ASSERT_EQUAL_INT(sizeof(value2),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_IPV6_GROUP, 0,
&value2, sizeof(value2)));
TEST_ASSERT(0 <= gnrc_netif_ipv6_group_idx(&ethernet_netif, &value2));
/* only one link layer group joined due to the same suffix */
TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet),
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_ethernet, l2_value[0],
sizeof(exp_ethernet)));
TEST_ASSERT_EQUAL_INT(sizeof(value1),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_IPV6_GROUP_LEAVE, 0,
&value1, sizeof(value1)));
TEST_ASSERT(0 > gnrc_netif_ipv6_group_idx(&ethernet_netif, &value1));
/* still in link layer group due to other IPv6 group */
TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet),
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
TEST_ASSERT_EQUAL_INT(0, memcmp(exp_ethernet, l2_value[0],
sizeof(exp_ethernet)));
TEST_ASSERT_EQUAL_INT(sizeof(value2),
gnrc_netapi_set(ethernet_netif.pid,
NETOPT_IPV6_GROUP_LEAVE, 0,
&value2, sizeof(value2)));
TEST_ASSERT(0 > gnrc_netif_ipv6_group_idx(&ethernet_netif, &value2));
/* no L2 group assigned */
TEST_ASSERT_EQUAL_INT(0,
gnrc_netapi_get(ethernet_netif.pid,
NETOPT_L2_GROUP, 0,
&l2_value, sizeof(l2_value)));
}
static void test_netapi_set__MAX_PACKET_SIZE(void)
{
uint16_t value = 57194;
@ -1574,11 +1740,16 @@ static Test *embunit_tests_gnrc_netif(void)
new_TestFixture(test_netapi_get__6LO_IPHC),
new_TestFixture(test_netapi_get__ADDRESS),
new_TestFixture(test_netapi_get__ADDRESS_LONG),
new_TestFixture(test_netapi_set_get__L2_GROUP),
new_TestFixture(test_netapi_set__L2_GROUP_LEAVE),
new_TestFixture(test_netapi_set__HOP_LIMIT),
new_TestFixture(test_netapi_set__IPV6_ADDR),
new_TestFixture(test_netapi_set__IPV6_ADDR_REMOVE),
new_TestFixture(test_netapi_set__IPV6_GROUP),
new_TestFixture(test_netapi_set__IPV6_GROUP__ethernet),
new_TestFixture(test_netapi_set__IPV6_GROUP_LEAVE),
new_TestFixture(test_netapi_set__IPV6_GROUP_LEAVE__ethernet),
new_TestFixture(test_netapi_set__IPV6_GROUP_LEAVE__ethernet_two_same),
new_TestFixture(test_netapi_set__MAX_PACKET_SIZE),
new_TestFixture(test_netapi_set__6LO_IPHC),
new_TestFixture(test_netapi_set__ADDRESS),
@ -1603,6 +1774,12 @@ int main(void)
_get_netdev_address);
netdev_test_set_set_cb((netdev_test_t *)ethernet_dev, NETOPT_ADDRESS,
_set_netdev_address);
netdev_test_set_get_cb((netdev_test_t *)ethernet_dev, NETOPT_L2_GROUP,
_get_netdev_l2_group);
netdev_test_set_set_cb((netdev_test_t *)ethernet_dev, NETOPT_L2_GROUP,
_set_netdev_l2_group);
netdev_test_set_set_cb((netdev_test_t *)ethernet_dev, NETOPT_L2_GROUP_LEAVE,
_set_netdev_l2_group_leave);
netdev_test_set_get_cb((netdev_test_t *)ieee802154_dev, NETOPT_ADDRESS,
_get_netdev_address);
netdev_test_set_set_cb((netdev_test_t *)ieee802154_dev, NETOPT_ADDRESS,
@ -1738,3 +1915,68 @@ static int _set_netdev_src_len(netdev_t *dev, const void *value,
}
return -ENOTSUP;
}
static int _get_netdev_l2_group(netdev_t *dev, void *value, size_t max_len)
{
(void)max_len;
if (dev == ethernet_dev) {
expect(max_len >= ETHERNET_ADDR_LEN);
int res = 0;
for (unsigned i = 0; res < (int)max_len && i < ETHERNET_GROUPS_MAX; i++) {
if (bf_isset(ethernet_groups_set, i)) {
memcpy(((uint8_t *)value) + res, ethernet_groups[i],
ETHERNET_ADDR_LEN);
res += ETHERNET_ADDR_LEN;
}
}
return res;
}
return -ENOTSUP;
}
static int _set_netdev_l2_group(netdev_t *dev, const void *value,
size_t value_len)
{
if (dev == ethernet_dev) {
int idx = -ENOMEM;
expect(value_len >= ETHERNET_ADDR_LEN);
for (unsigned i = 0; i < ETHERNET_GROUPS_MAX; i++) {
if (bf_isset(ethernet_groups_set, i)) {
if (memcmp(value, ethernet_groups[i],
sizeof(ethernet_groups[i])) == 0) {
return ETHERNET_ADDR_LEN;
}
}
else if (idx < 0) {
idx = i;
}
}
if (idx >= 0) {
memcpy(ethernet_groups[idx], value, sizeof(ethernet_groups[idx]));
bf_set(ethernet_groups_set, idx);
return ETHERNET_ADDR_LEN;
}
return idx;
}
return -ENOTSUP;
}
static int _set_netdev_l2_group_leave(netdev_t *dev, const void *value,
size_t value_len)
{
if (dev == ethernet_dev) {
expect(value_len >= ETHERNET_ADDR_LEN);
for (unsigned i = 0; i < ETHERNET_GROUPS_MAX; i++) {
if (bf_isset(ethernet_groups_set, i) &&
(memcmp(value, ethernet_groups[i],
sizeof(ethernet_groups[i])) == 0)) {
bf_unset(ethernet_groups_set, i);
}
}
return ETHERNET_ADDR_LEN;
}
return -ENOTSUP;
}

6
tests/l2util/Makefile.ci Normal file
View File

@ -0,0 +1,6 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-nano \
arduino-uno \
atmega328p \
#

View File

@ -32,6 +32,10 @@
#define TEST_EUI48_EUI64 { 0x21, 0x55, 0x31, 0xff, 0xfe, 0x02, 0x41, 0xfd }
#define TEST_EUI48_IID { 0x23, 0x55, 0x31, 0xff, 0xfe, 0x02, 0x41, 0xfd }
#define TEST_EUI64_IID { 0x23, 0x55, 0x31, 0x02, 0x41, 0xfd, 0xfb, 0xfd }
#define TEST_IPV6_GROUP { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x3f, 0x6c, 0xa1, 0xbb, 0xe5, 0x03, 0x6b, 0xe2 }
/* see https://tools.ietf.org/html/rfc2464#section-7 */
#define TEST_ETHERNET_GROUP { 0x33, 0x33, 0xe5, 0x03, 0x6b, 0xe2 }
static void test_eui64_from_addr__success(void)
{
@ -381,6 +385,37 @@ static void test_addr_len_from_l2ao__ENOTSUP(void)
&opt));
}
static void test_ipv6_group_to_l2group__success(void)
{
static const ipv6_addr_t test_group = {
.u8 = TEST_IPV6_GROUP,
};
static const eui48_t test_ethernet = {
.uint8 = TEST_ETHERNET_GROUP,
};
uint8_t res[L2UTIL_ADDR_MAX_LEN];
/* test Ethernet */
memset(res, 0, sizeof(res));
TEST_ASSERT_EQUAL_INT(sizeof(test_ethernet),
l2util_ipv6_group_to_l2_group(NETDEV_TYPE_ETHERNET,
&test_group, res));
TEST_ASSERT_EQUAL_INT(0, memcmp(&test_ethernet, res,
sizeof(test_ethernet)));
}
static void test_ipv6_group_to_l2group__ENOTSUP(void)
{
static const ipv6_addr_t test_group = {
.u8 = TEST_IPV6_GROUP,
};
uint8_t res[L2UTIL_ADDR_MAX_LEN];
TEST_ASSERT_EQUAL_INT(-ENOTSUP,
l2util_ipv6_group_to_l2_group(NETDEV_TYPE_UNKNOWN,
&test_group, res));
}
TestRef test_l2util(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
@ -395,6 +430,8 @@ TestRef test_l2util(void)
new_TestFixture(test_addr_len_from_l2ao__success),
new_TestFixture(test_addr_len_from_l2ao__EINVAL),
new_TestFixture(test_addr_len_from_l2ao__ENOTSUP),
new_TestFixture(test_ipv6_group_to_l2group__success),
new_TestFixture(test_ipv6_group_to_l2group__ENOTSUP),
};
EMB_UNIT_TESTCALLER(tests_l2util, NULL, NULL, fixtures);