Skip to content

Commit

Permalink
inetaddr: ipv6 link-local address auto configuration
Browse files Browse the repository at this point in the history
Referencing linux kernel(https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt),
dpvs supports 4 addr_gen_mode
  - eui64
  - none
  - stable-privacy
  - random

Signed-off-by: ywc689 <[email protected]>
  • Loading branch information
ywc689 committed Sep 9, 2024
1 parent 5a489eb commit d3daaa5
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 30 deletions.
3 changes: 3 additions & 0 deletions conf/dpvs.conf.items
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ ipv4_defs {
ipv6_defs {
disable off <off, on/off>
forwarding off <off, on/off>
addr_gen_mode eui64 <eui64,none,stable-privacy,random>
stable_secret "" <128-bit hexadecimal string, used in stable-privacy mode >
<stable_secret can be produced by `uuidgen | sed 's/-//g'>
route6 {
<init> method "hlist" <"hlist"/"lpm">
recycle_time 10 <10, 1-36000>
Expand Down
1 change: 1 addition & 0 deletions include/conf/inetaddr.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum {

/* leverage IFA_F_XXX in linux/if_addr.h*/
#define IFA_F_SAPOOL 0x10000 /* if address with sockaddr pool */
#define IFA_F_LINKLOCAL 0x20000 /* ipv6 link-local address */

/* ifa command flags */
#define IFA_F_OPS_VERBOSE 0x0001
Expand Down
8 changes: 7 additions & 1 deletion include/inetaddr.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@


enum {
IDEV_F_NO_IPV6 = 0x00000001
IDEV_F_NO_IPV6 = 0x00000001,
IDEV_F_NO_ROUTE = 0x00000002,
};

struct inet_device {
Expand Down Expand Up @@ -125,8 +126,13 @@ bool inet_chk_mcast_addr(int af, struct netif_port *dev,

void inet_ifaddr_dad_failure(struct inet_ifaddr *ifa);

struct inet_device *dev_get_idev(const struct netif_port *dev);

void idev_put(struct inet_device *idev);

int idev_addr_init(struct inet_device *idev);


int inet_addr_init(void);
int inet_addr_term(void);

Expand Down
16 changes: 16 additions & 0 deletions include/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,25 @@
#define IPV6
#define RTE_LOGTYPE_IPV6 RTE_LOGTYPE_USER1

enum ip6_addr_gen_mode {
IP6_ADDR_GEN_MODE_EUI64 = 1,
IP6_ADDR_GEN_MODE_NONE,
IP6_ADDR_GEN_MODE_STABLE_PRIVACY,
IP6_ADDR_GEN_MODE_RANDOM,
IP6_ADDR_GFN_MODE_MAX = 64,
};

struct ipv6_stable_secret {
bool initialized;
struct in6_addr secret;
};

struct ipv6_config {
unsigned disable:1;
unsigned forwarding:1;
unsigned addr_gen_mode:6;
struct ipv6_stable_secret secret_stable;
struct ipv6_stable_secret secret_random;
};

const struct ipv6_config *ip6_config_get(void);
Expand Down
13 changes: 13 additions & 0 deletions include/linux_ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,19 @@ static inline int ipv6_saddr_preferred(int type)
return 0;
}

static inline bool ipv6_reserved_interfaceid(const struct in6_addr *addr)
{
if ((addr->s6_addr32[2] | addr->s6_addr32[3]) == 0)
return true;
if (addr->s6_addr32[2] == htonl(0x02005eff) &&
((addr->s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
return true;
if (addr->s6_addr32[2] == htonl(0xfdffffff) &&
((addr->s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
return true;
return false;
}

#ifdef __DPVS__
/*functions below were edited from addrconf.c*/

Expand Down
150 changes: 130 additions & 20 deletions src/inetaddr.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*
*/
#include <assert.h>
#include <openssl/sha.h>
#include "dpdk.h"
#include "ctrl.h"
#include "netif.h"
Expand Down Expand Up @@ -76,14 +77,14 @@ static uint32_t ifa_msg_seq(void)
return counter++;
}

static inline struct inet_device *dev_get_idev(const struct netif_port *dev)
struct inet_device *dev_get_idev(const struct netif_port *dev)
{
assert(dev && dev->in_ptr);
rte_atomic32_inc(&dev->in_ptr->refcnt);
return dev->in_ptr;
}

static inline void idev_put(struct inet_device *idev)
void idev_put(struct inet_device *idev)
{
rte_atomic32_dec(&idev->refcnt);
}
Expand Down Expand Up @@ -233,12 +234,11 @@ static int ifa_add_del_mcast(struct inet_ifaddr *ifa, bool add, bool is_master)
}

/* add ipv6 multicast address after port start */
static int __idev_inet6_mcast_init(struct netif_port *dev)
static int __idev_inet6_mcast_init(struct inet_device *idev)
{
int err;
union inet_addr all_nodes, all_routers;
struct rte_ether_addr eaddr_nodes, eaddr_routers;
struct inet_device *idev = dev_get_idev(dev);
bool is_master = (rte_lcore_id() == g_master_lcore_id);

memset(&eaddr_nodes, 0, sizeof(eaddr_nodes));
Expand Down Expand Up @@ -270,7 +270,6 @@ static int __idev_inet6_mcast_init(struct netif_port *dev)
goto free_idev_routers;
}

idev_put(idev);
return EDPVS_OK;

free_idev_routers:
Expand All @@ -281,43 +280,154 @@ static int __idev_inet6_mcast_init(struct netif_port *dev)
free_idev_nodes:
idev_mc_del(AF_INET6, idev, &all_nodes);
errout:
idev_put(idev);
return err;
}

static int inet6_addr_gen_eui64(struct inet_device *idev, struct in6_addr *addr)
{
unsigned char hwaddr[6];
unsigned char *eui = &addr->s6_addr[8];

rte_memcpy(hwaddr, &idev->dev->addr, 6);
eui[0] = hwaddr[0] ^ 0x02;
eui[1] = hwaddr[1];
eui[2] = hwaddr[2];
eui[3] = 0xFF;
eui[4] = 0xFE;
eui[5] = hwaddr[3];
eui[6] = hwaddr[4];
eui[7] = hwaddr[5];

return EDPVS_OK;
}

static int inet6_addr_gen_stable(struct in6_addr secret, struct inet_device *idev, struct in6_addr *addr)
{
#define MAX_RETRY 8
struct in6_addr temp;
unsigned char md[SHA256_DIGEST_LENGTH];
struct {
struct in6_addr secret;
uint32_t prefix[2];
struct rte_ether_addr hwaddr;
uint8_t dad_count;
} __rte_packed data;
uint8_t dad_count = 0;

while (1) {
memset(&md, 0, sizeof(md));
memset(&data, 0, sizeof(data));
data.secret = secret;
data.prefix[0] = addr->s6_addr32[0];
data.prefix[1] = addr->s6_addr32[1];
data.hwaddr = idev->dev->addr;
data.dad_count = dad_count++;

SHA512((unsigned char*)&data, sizeof(data), md);
temp = *addr;
temp.s6_addr32[2] = *((uint32_t *)&md[0]);
temp.s6_addr32[3] = *((uint32_t *)&md[4]);
if (!ipv6_reserved_interfaceid(&temp))
break;
if (dad_count >= MAX_RETRY)
return EDPVS_RESOURCE;
}

*addr = temp;
return EDPVS_OK;
}

static int inet6_link_local_addr_gen(struct inet_device *idev, struct in6_addr *addr)
{
const struct ipv6_config *ip6cfg = ip6_config_get();

ipv6_addr_set(addr, htonl(0xFE800000), 0, 0, 0);
switch (ip6cfg->addr_gen_mode) {
case IP6_ADDR_GEN_MODE_EUI64:
return inet6_addr_gen_eui64(idev, addr);
case IP6_ADDR_GEN_MODE_NONE:
return EDPVS_DISABLED;
case IP6_ADDR_GEN_MODE_STABLE_PRIVACY:
if (ip6cfg->secret_stable.initialized)
return inet6_addr_gen_stable(ip6cfg->secret_stable.secret, idev, addr);
// fallthrough
case IP6_ADDR_GEN_MODE_RANDOM:
return inet6_addr_gen_stable(ip6cfg->secret_random.secret, idev, addr);
default:
return EDPVS_NOTSUPP;
}

return EDPVS_OK;
}

static int ifa_entry_add(const struct ifaddr_action *param);
static int __inet6_link_local_addr_config(struct inet_device *idev, const struct in6_addr *addr)
{
struct ifaddr_action param;

memset(&param, 0, sizeof(param));
fill_ifaddr_action(AF_INET6, idev->dev, (union inet_addr *)addr, 64, NULL,
0, 0, IFA_SCOPE_LINK, IFA_F_LINKLOCAL, INET_ADDR_ADD, &param);
return ifa_entry_add(&param);
}

struct idev_addr_init_args {
struct inet_device *idev;
struct in6_addr link_local_addr;
};

static int __idev_addr_init(void *args)
{
struct inet_device *idev = args;
assert(idev != NULL && idev->dev != NULL);
int err;
struct idev_addr_init_args *param = args;

assert(param && param->idev && param->idev->dev);

if (rte_lcore_id() >= DPVS_MAX_LCORE)
return EDPVS_OK;

return __idev_inet6_mcast_init(idev->dev);
err = __inet6_link_local_addr_config(param->idev, &param->link_local_addr);
if (err != EDPVS_OK)
return err;

return __idev_inet6_mcast_init(param->idev);
}

int idev_addr_init(struct inet_device *idev)
{
int err;
lcoreid_t cid;
lcoreid_t cid, tcid;
struct dpvs_msg *msg;
struct idev_addr_init_args args;

// only ipv6 needs address initialization now
if (ip6_config_get()->disable || (idev->flags & IDEV_F_NO_IPV6))
return EDPVS_OK;

if (rte_lcore_id() != rte_get_main_lcore())
if (idev->flags & IDEV_F_NO_ROUTE)
return EDPVS_OK;

cid = rte_lcore_id();
if (cid != rte_get_main_lcore())
return EDPVS_NOTSUPP;

args.idev = idev;
err = inet6_link_local_addr_gen(idev, &args.link_local_addr);
if (err != EDPVS_OK) {
if (EDPVS_DISABLED == err)
return EDPVS_OK;
return err;
}

// do it on master lcore
err = __idev_addr_init(idev);
err = __idev_addr_init(&args);
if (err != EDPVS_OK)
return err;

// do it on slave lcores
if (dpvs_state_get() == DPVS_STATE_NORMAL) {
msg = msg_make(MSG_TYPE_IFA_IDEVINIT, ifa_msg_seq(), DPVS_MSG_MULTICAST,
rte_lcore_id(), sizeof(idev), &idev);
cid, sizeof(args), &args);
if (unlikely(!msg))
return EDPVS_NOMEM;
err = multicast_msg_send(msg, DPVS_MSG_F_ASYNC, NULL);
Expand All @@ -327,9 +437,9 @@ int idev_addr_init(struct inet_device *idev)
}
msg_destroy(&msg);
} else {
rte_eal_mp_remote_launch(__idev_addr_init, idev, SKIP_MAIN);
RTE_LCORE_FOREACH_WORKER(cid) {
err = rte_eal_wait_lcore(cid);
rte_eal_mp_remote_launch(__idev_addr_init, &args, SKIP_MAIN);
RTE_LCORE_FOREACH_WORKER(tcid) {
err = rte_eal_wait_lcore(tcid);
if (unlikely(err < 0))
return err;
}
Expand Down Expand Up @@ -1381,13 +1491,13 @@ static int ifa_msg_sync_cb(struct dpvs_msg *msg)

static int ifa_msg_idevinit_cb(struct dpvs_msg *msg)
{
struct inet_device *idev;
struct idev_addr_init_args *param;

if (unlikely(!msg || msg->len != sizeof(idev)))
if (unlikely(!msg || msg->len != sizeof(*param)))
return EDPVS_INVAL;
idev = *((struct inet_device **)(msg->data));
param = (struct idev_addr_init_args *)(msg->data);

return __idev_addr_init(idev);
return __idev_addr_init(param);
}

static int __inet_addr_add(const struct ifaddr_action *param)
Expand Down
8 changes: 6 additions & 2 deletions src/ip_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -906,11 +906,15 @@ int ip_tunnel_dev_init(struct netif_port *dev)
{
int err;
struct ip_tunnel *tnl = netif_priv(dev);
struct inet_device *idev = dev_get_idev(tnl->dev);

err = idev_addr_init(tnl->dev->in_ptr);
if (err != EDPVS_OK)
err = idev_addr_init(idev);
if (err != EDPVS_OK) {
idev_put(idev);
return err;
}

idev_put(idev);
return EDPVS_OK;
}

Expand Down
Loading

0 comments on commit d3daaa5

Please sign in to comment.