mirror of
https://github.com/apache/nuttx.git
synced 2026-06-06 16:50:55 +08:00
net/netdev: Support multiple IPv6 addresses per device
Compatible with previous usage, because may network drivers are using old member name to print logs, and there's no significant need to change them now. Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
@@ -42,4 +42,8 @@ if(CONFIG_NETDOWN_NOTIFIER)
|
||||
list(APPEND SRCS netdown_notifier.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_NET_IPv6)
|
||||
list(APPEND SRCS netdev_ipv6.c)
|
||||
endif()
|
||||
|
||||
target_sources(net PRIVATE ${SRCS})
|
||||
|
||||
@@ -61,6 +61,24 @@ config NETDEV_IFINDEX
|
||||
When enabled, these option also enables the user interfaces:
|
||||
if_nametoindex() and if_indextoname().
|
||||
|
||||
config NETDEV_MULTIPLE_IPv6
|
||||
bool "Enable multiple IPv6 addresses support"
|
||||
default n
|
||||
select NETDEV_IFINDEX
|
||||
depends on NET_IPv6
|
||||
---help---
|
||||
Enable support for multiple IPv6 addresses per network device.
|
||||
|
||||
config NETDEV_MAX_IPv6_ADDR
|
||||
int "Maximum number of IPv6 addresses per network device"
|
||||
range 2 255
|
||||
default 2
|
||||
depends on NETDEV_MULTIPLE_IPv6
|
||||
---help---
|
||||
Maximum number of IPv6 addresses that can be assigned to a single
|
||||
network device. Normally a link-local address and a global address
|
||||
are needed.
|
||||
|
||||
config NETDOWN_NOTIFIER
|
||||
bool "Support network down notifications"
|
||||
default n
|
||||
|
||||
@@ -34,6 +34,10 @@ ifeq ($(CONFIG_NETDOWN_NOTIFIER),y)
|
||||
SOCK_CSRCS += netdown_notifier.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_NET_IPv6),y)
|
||||
NETDEV_CSRCS += netdev_ipv6.c
|
||||
endif
|
||||
|
||||
# Include netdev build support
|
||||
|
||||
DEPPATH += --dep-path netdev
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
/****************************************************************************
|
||||
* net/netdev/netdev_ipv6.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <nuttx/net/netdev.h>
|
||||
|
||||
#include "inet/inet.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Defined in Section 2.7 of RFC4291 */
|
||||
|
||||
#define IPv6_SCOPE_INTERFACE_LOCAL 0x1
|
||||
#define IPv6_SCOPE_LINK_LOCAL 0x2
|
||||
#define IPv6_SCOPE_ADMIN_LOCAL 0x4
|
||||
#define IPv6_SCOPE_SITE_LOCAL 0x5
|
||||
#define IPv6_SCOPE_ORGANIZATION_LOCAL 0x8
|
||||
#define IPv6_SCOPE_GLOBAL 0xe
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_get_scope
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
|
||||
static uint8_t netdev_ipv6_get_scope(const net_ipv6addr_t addr)
|
||||
{
|
||||
if (net_is_addr_mcast(addr))
|
||||
{
|
||||
/* As defined in Section 2.7 of RFC4291:
|
||||
* | 8 | 4 | 4 | 112 bits |
|
||||
* +------ -+----+----+---------------------------------------------+
|
||||
* |11111111|flgs|scop| group ID |
|
||||
* +--------+----+----+---------------------------------------------+
|
||||
*/
|
||||
|
||||
return NTOHS(addr[0]) & 0x000f;
|
||||
}
|
||||
|
||||
if (net_is_addr_linklocal(addr))
|
||||
{
|
||||
return IPv6_SCOPE_LINK_LOCAL;
|
||||
}
|
||||
|
||||
if (net_is_addr_sitelocal(addr))
|
||||
{
|
||||
return IPv6_SCOPE_SITE_LOCAL;
|
||||
}
|
||||
|
||||
return IPv6_SCOPE_GLOBAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_add/del
|
||||
*
|
||||
* Description:
|
||||
* Add or delete an IPv6 address on the network device
|
||||
*
|
||||
* Returned Value:
|
||||
* OK - Success
|
||||
* -EINVAL - Invalid prefix length
|
||||
* -EADDRNOTAVAIL - Delete on non-existent address
|
||||
*
|
||||
* Assumptions:
|
||||
* The caller has locked the network.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int netdev_ipv6_add(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
|
||||
unsigned int preflen)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[0];
|
||||
#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
|
||||
uint8_t scope;
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/* Verify the prefix length */
|
||||
|
||||
if (preflen > 128)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
|
||||
/* Avoid duplicate address. */
|
||||
|
||||
ifaddr = netdev_ipv6_lookup(dev, addr, false);
|
||||
if (ifaddr != NULL)
|
||||
{
|
||||
/* Check if net mask is the same. */
|
||||
|
||||
if (net_ipv6_mask2pref(ifaddr->mask) == preflen)
|
||||
{
|
||||
nwarn("WARNING: Trying to add same IPv6 address on net device! "
|
||||
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%d\n",
|
||||
NTOHS(addr[0]), NTOHS(addr[1]), NTOHS(addr[2]),
|
||||
NTOHS(addr[3]), NTOHS(addr[4]), NTOHS(addr[5]),
|
||||
NTOHS(addr[6]), NTOHS(addr[7]), preflen);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/* Not exactly the same, update the net mask.
|
||||
* REVISIT: Currently try to keep logic same as previous, which always
|
||||
* allows to override the address. But not sure if it's good.
|
||||
*/
|
||||
|
||||
net_ipv6_pref2mask(preflen, ifaddr->mask);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Now we start to find a proper slot to put this address. */
|
||||
|
||||
ifaddr = &dev->d_ipv6[0]; /* Set default to a valid address. */
|
||||
scope = netdev_ipv6_get_scope(addr);
|
||||
|
||||
for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *current = &dev->d_ipv6[i];
|
||||
|
||||
/* Select empty address. */
|
||||
|
||||
if (net_ipv6addr_cmp(current->addr, g_ipv6_unspecaddr))
|
||||
{
|
||||
ifaddr = current;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Select address with same scope. */
|
||||
|
||||
if (netdev_ipv6_get_scope(current->addr) == scope)
|
||||
{
|
||||
ifaddr = current;
|
||||
continue; /* Good slot, but maybe we have empty slot later. */
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_NETDEV_MULTIPLE_IPv6 */
|
||||
|
||||
net_ipv6addr_copy(ifaddr->addr, addr);
|
||||
net_ipv6_pref2mask(preflen, ifaddr->mask);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int netdev_ipv6_del(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
|
||||
unsigned int preflen)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *ifaddr;
|
||||
|
||||
/* Verify the prefix length */
|
||||
|
||||
if (preflen > 128)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find the matching address entry */
|
||||
|
||||
ifaddr = netdev_ipv6_lookup(dev, addr, false);
|
||||
if (ifaddr == NULL)
|
||||
{
|
||||
/* The address does not exist on the device */
|
||||
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
if (net_ipv6_mask2pref(ifaddr->mask) != preflen)
|
||||
{
|
||||
/* Prefix length does not match, regard as not found (same as Linux) */
|
||||
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
/* Delete the address */
|
||||
|
||||
net_ipv6addr_copy(ifaddr->addr, g_ipv6_unspecaddr);
|
||||
net_ipv6addr_copy(ifaddr->mask, g_ipv6_unspecaddr);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_srcaddr/srcifaddr
|
||||
*
|
||||
* Description:
|
||||
* Get the source IPv6 address (RFC6724).
|
||||
*
|
||||
* Returned Value:
|
||||
* A pointer to the IPv6 address is returned on success. It will never be
|
||||
* NULL, but can be an address containing g_ipv6_unspecaddr.
|
||||
*
|
||||
* Assumptions:
|
||||
* The caller has locked the network.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR const uint16_t *netdev_ipv6_srcaddr(FAR struct net_driver_s *dev,
|
||||
const net_ipv6addr_t dst)
|
||||
{
|
||||
return netdev_ipv6_srcifaddr(dev, dst)->addr;
|
||||
}
|
||||
|
||||
FAR const struct netdev_ifaddr6_s *
|
||||
netdev_ipv6_srcifaddr(FAR struct net_driver_s *dev, const net_ipv6addr_t dst)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *best = &dev->d_ipv6[0]; /* Don't be NULL */
|
||||
#ifdef CONFIG_NETDEV_MULTIPLE_IPv6
|
||||
uint8_t scope_dst = netdev_ipv6_get_scope(dst);
|
||||
uint8_t scope_best = 0; /* All scope is larget than 0 */
|
||||
uint8_t pref_best = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *current = &dev->d_ipv6[i];
|
||||
uint8_t scope_cur;
|
||||
uint8_t pref_cur;
|
||||
|
||||
/* Skip empty address */
|
||||
|
||||
if (net_ipv6addr_cmp(current->addr, g_ipv6_unspecaddr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Rule 1: Prefer same address */
|
||||
|
||||
if (net_ipv6addr_cmp(dst, current->addr))
|
||||
{
|
||||
best = current;
|
||||
break;
|
||||
}
|
||||
|
||||
scope_cur = netdev_ipv6_get_scope(current->addr);
|
||||
pref_cur = net_ipv6_common_pref(current->addr, dst);
|
||||
|
||||
/* Rule 2: Prefer appropriate scope */
|
||||
|
||||
if (scope_cur != scope_best)
|
||||
{
|
||||
/* According to RFC6724:
|
||||
* If Scope(SA) < Scope(SB):
|
||||
* If Scope(SA) < Scope(D), then prefer SB and otherwise prefer SA
|
||||
* If Scope(SB) < Scope(SA):
|
||||
* If Scope(SB) < Scope(D), then prefer SA and otherwise prefer SB
|
||||
* Let Scope(SA)->Scope(cur), Scope(SB)->Scope(best) in our case.
|
||||
*/
|
||||
|
||||
if ((scope_cur < scope_best && scope_cur >= scope_dst) ||
|
||||
(scope_best < scope_cur && scope_best < scope_dst))
|
||||
{
|
||||
best = current;
|
||||
scope_best = scope_cur;
|
||||
pref_best = pref_cur;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Rule 3: Avoid deprecated and optimistic addresses
|
||||
* [Not implemented: Need DAD & address type support]
|
||||
* Rule 4: Prefer home address
|
||||
* [Not implemented: Need MIP6]
|
||||
* Rule 5: Prefer outgoing interface
|
||||
* [Already satisfied: We already have the device]
|
||||
* Rule 6: Prefer matching label
|
||||
* [Not implemented: Need policy table support]
|
||||
* [Note: Neither lwIP nor Zephyr supports policy table yet]
|
||||
* Rule 7: Prefer temporary addresses
|
||||
* [Not implemented: Need DAD & temporary addresses support]
|
||||
*/
|
||||
|
||||
/* Rule 8: Use longest matching prefix */
|
||||
|
||||
if (pref_cur > pref_best)
|
||||
{
|
||||
best = current;
|
||||
scope_best = scope_cur;
|
||||
pref_best = pref_cur;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_NETDEV_MULTIPLE_IPv6 */
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_lladdr
|
||||
*
|
||||
* Description:
|
||||
* Get the link-local address of the network device.
|
||||
*
|
||||
* Returned Value:
|
||||
* A pointer to the link-local address is returned on success.
|
||||
* NULL is returned if the address is not found on the device.
|
||||
*
|
||||
* Assumptions:
|
||||
* The caller has locked the network.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR const uint16_t *netdev_ipv6_lladdr(FAR struct net_driver_s *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
|
||||
|
||||
if (net_is_addr_linklocal(ifaddr->addr))
|
||||
{
|
||||
return ifaddr->addr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_lookup
|
||||
*
|
||||
* Description:
|
||||
* Look up an IPv6 address in the network device's IPv6 addresses
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The network device to use in the lookup
|
||||
* addr - The IPv6 address to be looked up
|
||||
* maskcmp - If true, then the IPv6 address is compared to the network
|
||||
* device's IPv6 addresses with mask compare.
|
||||
* If false, then the IPv6 address should be exactly the same as
|
||||
* the network device's IPv6 address.
|
||||
*
|
||||
* Returned Value:
|
||||
* A pointer to the matching IPv6 address entry is returned on success.
|
||||
* NULL is returned if the IPv6 address is not found in the device.
|
||||
*
|
||||
* Assumptions:
|
||||
* The caller has locked the network.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
FAR struct netdev_ifaddr6_s *
|
||||
netdev_ipv6_lookup(FAR struct net_driver_s *dev, const net_ipv6addr_t addr,
|
||||
bool maskcmp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
|
||||
|
||||
/* Skip empty address */
|
||||
|
||||
if (net_ipv6addr_cmp(ifaddr->addr, g_ipv6_unspecaddr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the address matches */
|
||||
|
||||
if (maskcmp)
|
||||
{
|
||||
if (net_ipv6addr_maskcmp(addr, ifaddr->addr, ifaddr->mask))
|
||||
{
|
||||
return ifaddr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (net_ipv6addr_cmp(addr, ifaddr->addr))
|
||||
{
|
||||
return ifaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match found */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: netdev_ipv6_foreach
|
||||
*
|
||||
* Description:
|
||||
* Enumerate each IPv6 address on a network device. This function will
|
||||
* terminate when either (1) all addresses have been enumerated or (2) when
|
||||
* a callback returns any non-zero value.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - The network device
|
||||
* callback - Will be called for each IPv6 address
|
||||
* arg - Opaque user argument passed to callback()
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero: Enumeration completed
|
||||
* Non-zero: Enumeration terminated early by callback
|
||||
*
|
||||
* Assumptions:
|
||||
* The network is locked.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int netdev_ipv6_foreach(FAR struct net_driver_s *dev,
|
||||
devif_ipv6_callback_t callback, FAR void *arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (callback == NULL)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_NETDEV_MAX_IPv6_ADDR; i++)
|
||||
{
|
||||
FAR struct netdev_ifaddr6_s *ifaddr = &dev->d_ipv6[i];
|
||||
|
||||
if (!net_ipv6addr_cmp(ifaddr->addr, g_ipv6_unspecaddr))
|
||||
{
|
||||
int ret = callback(dev, ifaddr, arg);
|
||||
if (ret != 0) /* Stop on any error and return it */
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -207,4 +207,42 @@ uint8_t net_ipv6_mask2pref(FAR const uint16_t *mask)
|
||||
return preflen;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: net_ipv6_common_pref
|
||||
*
|
||||
* Description:
|
||||
* Calculate the common prefix length of two IPv6 addresses.
|
||||
*
|
||||
* Input Parameters:
|
||||
* a1,a2 Points to IPv6 addresses in the form of uint16_t[8]
|
||||
*
|
||||
* Returned Value:
|
||||
* The common prefix length, range 0-128 on success; This function will
|
||||
* not fail.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
uint8_t net_ipv6_common_pref(FAR const uint16_t *a1, FAR const uint16_t *a2)
|
||||
{
|
||||
uint8_t preflen;
|
||||
int i;
|
||||
|
||||
/* Count the leading same 16-bit groups */
|
||||
|
||||
for (i = 0, preflen = 0; i < 8 && a1[i] == a2[i]; i++, preflen += 16);
|
||||
|
||||
/* Now i either, (1) indexes past the end of the mask, or (2) is the index
|
||||
* to the first half-word that is not equal between the addresses.
|
||||
*/
|
||||
|
||||
if (i < 8)
|
||||
{
|
||||
preflen += net_msbits16(NTOHS(~(a1[i] ^ a2[i])));
|
||||
}
|
||||
|
||||
/* Return the prefix length */
|
||||
|
||||
return preflen;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_IPv6 */
|
||||
|
||||
@@ -187,6 +187,25 @@ void net_getrandom(FAR void *bytes, size_t nbytes);
|
||||
uint8_t net_ipv4_mask2pref(in_addr_t mask);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: net_ipv6_common_pref
|
||||
*
|
||||
* Description:
|
||||
* Calculate the common prefix length of two IPv6 addresses.
|
||||
*
|
||||
* Input Parameters:
|
||||
* a1,a2 Points to IPv6 addresses in the form of uint16_t[8]
|
||||
*
|
||||
* Returned Value:
|
||||
* The common prefix length, range 0-128 on success; This function will
|
||||
* not fail.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NET_IPv6
|
||||
uint8_t net_ipv6_common_pref(FAR const uint16_t *a1, FAR const uint16_t *a2);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: net_ipv6_mask2pref
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user