diff --git a/conf/dpvs.conf.items b/conf/dpvs.conf.items index 448b4004..80f2e123 100644 --- a/conf/dpvs.conf.items +++ b/conf/dpvs.conf.items @@ -194,6 +194,9 @@ ipv4_defs { ipv6_defs { disable off forwarding off + addr_gen_mode eui64 + stable_secret "" <128-bit hexadecimal string, used in stable-privacy mode > + route6 { method "hlist" <"hlist"/"lpm"> recycle_time 10 <10, 1-36000> diff --git a/include/conf/inetaddr.h b/include/conf/inetaddr.h index 7a82b0d9..252e6f3e 100644 --- a/include/conf/inetaddr.h +++ b/include/conf/inetaddr.h @@ -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 diff --git a/include/inetaddr.h b/include/inetaddr.h index 2e1e309a..a96b6251 100644 --- a/include/inetaddr.h +++ b/include/inetaddr.h @@ -28,7 +28,8 @@ enum { - IDEV_F_NO_IPV6 = 0x00000001 + IDEV_F_NO_IPV6 = 0x00000001, + IDEV_F_NO_ROUTE = 0x00000002, }; struct inet_device { @@ -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); diff --git a/include/ipv6.h b/include/ipv6.h index 8cf53b02..7a95736f 100644 --- a/include/ipv6.h +++ b/include/ipv6.h @@ -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); diff --git a/include/linux_ipv6.h b/include/linux_ipv6.h index 05bc4ae3..6d19b1b3 100644 --- a/include/linux_ipv6.h +++ b/include/linux_ipv6.h @@ -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*/ diff --git a/src/inetaddr.c b/src/inetaddr.c index 124c75e0..fb833436 100644 --- a/src/inetaddr.c +++ b/src/inetaddr.c @@ -16,6 +16,7 @@ * */ #include +#include #include "dpdk.h" #include "ctrl.h" #include "netif.h" @@ -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); } @@ -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)); @@ -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: @@ -281,43 +280,156 @@ 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; + union { + unsigned char data[SHA256_DIGEST_LENGTH]; + uint32_t data_word[2]; + } md; + 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; + + 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; + while (1) { + data.dad_count = dad_count++; + memset(&md, 0, sizeof(md)); + SHA512((const unsigned char*)&data, sizeof(data), md.data); + temp = *addr; + temp.s6_addr32[2] = md.data_word[0]; + temp.s6_addr32[3] = md.data_word[1]; + 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(¶m, 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, ¶m); + return ifa_entry_add(¶m); +} + +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, ¶m->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); @@ -327,9 +439,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; } @@ -1381,13 +1493,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) diff --git a/src/ip_tunnel.c b/src/ip_tunnel.c index 415b9bfb..e26f3eee 100644 --- a/src/ip_tunnel.c +++ b/src/ip_tunnel.c @@ -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; } diff --git a/src/ipv6/ipv6.c b/src/ipv6/ipv6.c index 3fa58485..37da08fb 100644 --- a/src/ipv6/ipv6.c +++ b/src/ipv6/ipv6.c @@ -112,7 +112,7 @@ static void ip6_prot_init(void) rte_rwlock_write_unlock(&inet6_prot_lock); } -static void ip6_conf_forward(vector_t tokens) +static void ip6_forwarding_handler(vector_t tokens) { char *str = set_value(tokens); @@ -130,7 +130,7 @@ static void ip6_conf_forward(vector_t tokens) FREE_PTR(str); } -static void ip6_conf_disable(vector_t tokens) +static void ip6_disable_handler(vector_t tokens) { char *str = set_value(tokens); @@ -143,11 +143,78 @@ static void ip6_conf_disable(vector_t tokens) else RTE_LOG(WARNING, IPV6, "invalid ipv6:disable %s\n", str); - RTE_LOG(INFO, IPV6, "ipv6: %s\n", ip6_configs.disable ? "disabled" : "enabled"); + RTE_LOG(INFO, IPV6, "ipv6:disable=%s\n", ip6_configs.disable ? "disabled" : "enabled"); FREE_PTR(str); } +static void ip6_addr_gen_mode_handler(vector_t tokens) +{ + char *str = set_value(tokens); + + assert(str); + + if (!strcasecmp(str, "eui64")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_EUI64; + else if (!strcasecmp(str, "none")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_NONE; + else if (!strcasecmp(str, "stable-privacy")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_STABLE_PRIVACY; + else if (!strcasecmp(str, "random")) + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_RANDOM; + else + RTE_LOG(WARNING, IPV6, "invalid ipv6:addr_gen_mode:%s\n", str); + + RTE_LOG(INFO, IPV6, "ipv6:addr_gen_mode=%s\n", str); + + FREE_PTR(str); +} + +static void ip6_stable_secret_handler(vector_t tokens) +{ + bool valid = true; + size_t i, len; + char *str = set_value(tokens); + + assert(str); + len = strlen(str); + if (len < 32) { + valid = false; + } else { + for (i = 0; i < 32; i++) { + if (!isxdigit(str[i])) { + valid = false; + break; + } + } + } + if (!valid) { + RTE_LOG(WARNING, IPV6, "invalid ipv6:stable_secret %s, " + "a 128-bit hexadecimal string required\n", str); + FREE_PTR(str); + return; + } + + if (hexstr2binary(str, 32, (uint8_t *)(&ip6_configs.secret_stable.secret), 16) == 16) + ip6_configs.secret_stable.initialized = true; + else + RTE_LOG(WARNING, IPV6, "fail to tranlate ipv6:stable_secret %s into binary\n", str); + RTE_LOG(INFO, IPV6, "ipv6:stable_secret configured"); + + FREE_PTR(str); +} + +static inline void ip6_gen_mode_random_init(void) +{ + const char hex_chars[] = "0123456789abcdef"; + char *buf = (char *)(&ip6_configs.secret_random.secret); + int i; + + for (i = 0; i < 16; i++) + buf[i] = hex_chars[random() % 16]; + ip6_configs.secret_random.initialized = true; +} + /* refer linux:ip6_input_finish() */ static int ip6_local_in_fin(struct rte_mbuf *mbuf) { @@ -660,6 +727,8 @@ int ipv6_init(void) /* htons, cpu_to_be16 not work when struct initialization :( */ ip6_pkt_type.type = htons(RTE_ETHER_TYPE_IPV6); + ip6_gen_mode_random_init(); + err = netif_register_pkt(&ip6_pkt_type); if (err) goto reg_pkt_err; @@ -821,6 +890,8 @@ void ipv6_keyword_value_init(void) /* KW_TYPE NORMAL keyword */ ip6_configs.forwarding = 0; ip6_configs.disable = 0; + ip6_configs.addr_gen_mode = IP6_ADDR_GEN_MODE_EUI64; + ip6_configs.secret_stable.initialized = false; route6_keyword_value_init(); } @@ -828,8 +899,10 @@ void ipv6_keyword_value_init(void) void install_ipv6_keywords(void) { install_keyword_root("ipv6_defs", NULL); - install_keyword("forwarding", ip6_conf_forward, KW_TYPE_NORMAL); - install_keyword("disable", ip6_conf_disable, KW_TYPE_NORMAL); + install_keyword("forwarding", ip6_forwarding_handler, KW_TYPE_NORMAL); + install_keyword("disable", ip6_disable_handler, KW_TYPE_NORMAL); + install_keyword("addr_gen_mode", ip6_addr_gen_mode_handler, KW_TYPE_NORMAL); + install_keyword("stable_secret", ip6_stable_secret_handler, KW_TYPE_NORMAL); install_route6_keywords(); } diff --git a/src/netif.c b/src/netif.c index 680436ca..5d8fe0b3 100644 --- a/src/netif.c +++ b/src/netif.c @@ -4164,6 +4164,7 @@ static int relate_bonding_device(void) } sport->type = PORT_TYPE_BOND_SLAVE; sport->bond->slave.master = mport; + sport->in_ptr->flags |= IDEV_F_NO_ROUTE; } mport->bond->master.slave_nb = i; } diff --git a/src/vlan.c b/src/vlan.c index 0d350d07..955b0b84 100644 --- a/src/vlan.c +++ b/src/vlan.c @@ -78,11 +78,15 @@ static int alloc_vlan_info(struct netif_port *dev) static int vlan_dev_init(struct netif_port *dev) { int err; + struct inet_device *idev = dev_get_idev(dev); - err = idev_addr_init(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; }