diff --git a/doc/man/man5/keepalived.conf.5.in b/doc/man/man5/keepalived.conf.5.in index ce8f4e6a8f..c6e44123cf 100644 --- a/doc/man/man5/keepalived.conf.5.in +++ b/doc/man/man5/keepalived.conf.5.in @@ -520,8 +520,8 @@ possibly following any cleanup actions needed. # iptables. If so, then the ipset names can be specified, defaults # as below. If no names are specified, ipsets will not be used, # otherwise any omitted names will be constructed by adding "_if" - # and/or "6" and _igmp/_mld to previously specified names. - \fBvrrp_ipsets \fR[keepalived [keepalived6 [keepalived_if6 [keepalived_igmp [keepalived_mld]]]]] + # and/or "6" and _igmp/_mld/_nd to previously specified names. + \fBvrrp_ipsets \fR[keepalived [keepalived6 [keepalived_if6 [keepalived_igmp [keepalived_mld [keepalived_vmac_nd]]]]]] # An alternative to moving IGMP messages from VMACs to their parent interfaces # is to disable them altogether in the kernel by setting diff --git a/keepalived/core/global_data.c b/keepalived/core/global_data.c index 8f67dcd1ae..7288651267 100644 --- a/keepalived/core/global_data.c +++ b/keepalived/core/global_data.c @@ -399,6 +399,9 @@ free_global_data(data_t * data) FREE_CONST_PTR(data->vrrp_ipset_address_iface6); FREE_CONST_PTR(data->vrrp_ipset_igmp); FREE_CONST_PTR(data->vrrp_ipset_mld); +#ifdef _HAVE_VRRP_VMAC_ + FREE_CONST_PTR(data->vrrp_ipset_vmac_nd); +#endif #endif #endif #ifdef _WITH_NFTABLES_ @@ -691,6 +694,10 @@ dump_global_data(FILE *fp, data_t * data) conf_write(fp," ipset IGMP set = %s", data->vrrp_ipset_igmp); if (data->vrrp_ipset_mld) conf_write(fp," ipset MLD set = %s", data->vrrp_ipset_mld); +#ifdef _HAVE_VRRP_VMAC_ + if (data->vrrp_ipset_vmac_nd) + conf_write(fp," ipset ND set = %s", data->vrrp_ipset_vmac_nd); +#endif } #endif } diff --git a/keepalived/core/global_parser.c b/keepalived/core/global_parser.c index a3f23290a2..422d027844 100644 --- a/keepalived/core/global_parser.c +++ b/keepalived/core/global_parser.c @@ -1097,6 +1097,9 @@ vrrp_ipsets_handler(const vector_t *strvec) FREE_CONST_PTR(global_data->vrrp_ipset_address_iface6); FREE_CONST_PTR(global_data->vrrp_ipset_igmp); FREE_CONST_PTR(global_data->vrrp_ipset_mld); +#ifdef _HAVE_VRRP_VMAC_ + FREE_CONST_PTR(global_data->vrrp_ipset_vmac_nd); +#endif if (vector_size(strvec) < 2) { global_data->using_ipsets = false; @@ -1115,22 +1118,21 @@ vrrp_ipsets_handler(const vector_t *strvec) return; } global_data->vrrp_ipset_address6 = STRDUP(strvec_slot(strvec,2)); - } - else { + } else { /* No second set specified, copy first name and add "6" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[IPSET_MAXNAMELEN - 2] = '\0'; strcat(set_name, "6"); global_data->vrrp_ipset_address6 = STRDUP(set_name); } + if (vector_size(strvec) >= 4) { if (strlen(strvec_slot(strvec,3)) >= IPSET_MAXNAMELEN - 1) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error : ipset IPv6 address_iface name too long - ignored"); return; } global_data->vrrp_ipset_address_iface6 = STRDUP(strvec_slot(strvec,3)); - } - else { + } else { /* No third set specified, copy second name and add "_if6" */ strcpy_safe(set_name, global_data->vrrp_ipset_address6); len = strlen(set_name); @@ -1147,28 +1149,43 @@ vrrp_ipsets_handler(const vector_t *strvec) return; } global_data->vrrp_ipset_igmp = STRDUP(strvec_slot(strvec,4)); - } - else { + } else { /* No second set specified, copy first name and add "_igmp" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[sizeof(set_name) - 6] = '\0'; strcat(set_name, "_igmp"); global_data->vrrp_ipset_igmp = STRDUP(set_name); } + if (vector_size(strvec) >= 6) { if (strlen(strvec_slot(strvec,5)) >= IPSET_MAXNAMELEN - 1) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error : ipset MLD name too long - ignored"); return; } global_data->vrrp_ipset_mld = STRDUP(strvec_slot(strvec,5)); - } - else { + } else { /* No second set specified, copy first name and add "_mld" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[sizeof(set_name) - 5] = '\0'; strcat(set_name, "_mld"); global_data->vrrp_ipset_mld = STRDUP(set_name); } + +#ifdef _HAVE_VRRP_VMAC_ + if (vector_size(strvec) >= 7) { + if (strlen(strvec_slot(strvec,6)) >= IPSET_MAXNAMELEN - 1) { + report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error : ipset ND name too long - ignored"); + return; + } + global_data->vrrp_ipset_vmac_nd = STRDUP(strvec_slot(strvec,6)); + } else { + /* No second set specified, copy first name and add "_nd" */ + strcpy_safe(set_name, global_data->vrrp_ipset_address); + set_name[sizeof(set_name) - 5] = '\0'; + strcat(set_name, "_nd"); + global_data->vrrp_ipset_vmac_nd = STRDUP(set_name); + } +#endif } #endif #elif defined _WITH_NFTABLES_ diff --git a/keepalived/core/keepalived_netlink.c b/keepalived/core/keepalived_netlink.c index 9a42a2724f..2a855fa51c 100644 --- a/keepalived/core/keepalived_netlink.c +++ b/keepalived/core/keepalived_netlink.c @@ -2004,6 +2004,7 @@ netlink_if_link_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct n if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); +log_message(LOG_INFO, "Got netlink new message for %s", name); /* Skip it if already exists */ ifp = if_get_by_ifname(name, IF_CREATE_NETLINK); diff --git a/keepalived/include/global_data.h b/keepalived/include/global_data.h index 6d15899ede..1e81586cb1 100644 --- a/keepalived/include/global_data.h +++ b/keepalived/include/global_data.h @@ -179,6 +179,9 @@ typedef struct _data { const char *vrrp_ipset_address_iface6; const char *vrrp_ipset_igmp; const char *vrrp_ipset_mld; +#ifdef _HAVE_VRRP_VMAC_ + const char *vrrp_ipset_vmac_nd; +#endif #endif #endif #ifdef _WITH_NFTABLES_ diff --git a/keepalived/include/vrrp_ipset.h b/keepalived/include/vrrp_ipset.h index fd06885756..56f46c2dea 100644 --- a/keepalived/include/vrrp_ipset.h +++ b/keepalived/include/vrrp_ipset.h @@ -40,6 +40,7 @@ extern void* ipset_session_start(void); extern void ipset_session_end(void *); extern void ipset_entry(void *, int, const ip_address_t*); extern void ipset_entry_igmp(void*, int, const char *, uint8_t); +extern void ipset_entry_nd(void*, int, const interface_t *); extern void set_default_ipsets(void); extern void disable_ipsets(void); diff --git a/keepalived/vrrp/vrrp_ipset.c b/keepalived/vrrp/vrrp_ipset.c index e67b7bded1..ff86a7c601 100644 --- a/keepalived/vrrp/vrrp_ipset.c +++ b/keepalived/vrrp/vrrp_ipset.c @@ -187,7 +187,11 @@ has_ipset_setname(struct ipset_session* session, const char *setname) static bool create_sets(struct ipset_session **session, const char* addr4, const char* addr6, const char* addr_if6, - const char *igmp, const char *mld, bool is_reload) + const char *igmp, const char *mld, +#ifndef _HAVE_VRRP_VMAC_ + __attribute__((unused)) +#endif + const char *vmac_nd, bool is_reload) { if (!*session) #ifdef LIBIPSET_PRE_V7_COMPAT @@ -231,6 +235,13 @@ create_sets(struct ipset_session **session, const char* addr4, const char* addr6 ipset_create(*session, mld, "hash:net,iface", NFPROTO_IPV6); } +#ifdef _HAVE_VRRP_VMAC_ + if (vmac_nd) { + if (!is_reload || !has_ipset_setname(*session, vmac_nd)) + ipset_create(*session, vmac_nd, "hash:net,iface", NFPROTO_IPV6); + } +#endif + return true; } @@ -345,8 +356,12 @@ remove_ipsets(struct ipset_session **session, uint8_t family, bool vip_sets) } else { if (family == AF_INET) ipset_destroy(*session, global_data->vrrp_ipset_igmp); - else + else { ipset_destroy(*session, global_data->vrrp_ipset_mld); +#ifdef _HAVE_VRRP_VMAC_ + ipset_destroy(*session, global_data->vrrp_ipset_vmac_nd); +#endif + } } return true; @@ -367,17 +382,21 @@ remove_igmp_ipsets(struct ipset_session **session, uint8_t family) bool add_vip_ipsets(struct ipset_session **session, uint8_t family, bool is_reload) { if (family == AF_INET) - return create_sets(session, global_data->vrrp_ipset_address, NULL, NULL, NULL, NULL, is_reload); + return create_sets(session, global_data->vrrp_ipset_address, NULL, NULL, NULL, NULL, NULL, is_reload); - return create_sets(session, NULL, global_data->vrrp_ipset_address6, global_data->vrrp_ipset_address_iface6, NULL, NULL, is_reload); + return create_sets(session, NULL, global_data->vrrp_ipset_address6, global_data->vrrp_ipset_address_iface6, NULL, NULL, NULL, is_reload); } bool add_igmp_ipsets(struct ipset_session **session, uint8_t family, bool is_reload) { if (family == AF_INET) - return create_sets(session, NULL, NULL, NULL, global_data->vrrp_ipset_igmp, NULL, is_reload); + return create_sets(session, NULL, NULL, NULL, global_data->vrrp_ipset_igmp, NULL, NULL, is_reload); - return create_sets(session, NULL, NULL, NULL, NULL, global_data->vrrp_ipset_mld, is_reload); + return create_sets(session, NULL, NULL, NULL, NULL, global_data->vrrp_ipset_mld, NULL, is_reload) +#ifdef _HAVE_VRRP_VMAC_ + && create_sets(session, NULL, NULL, NULL, NULL, NULL, global_data->vrrp_ipset_vmac_nd, is_reload) +#endif + ; } void* ipset_session_start(void) @@ -431,6 +450,17 @@ void ipset_entry_igmp(void* vsession, int cmd, const char* ifname, uint8_t famil do_ipset_cmd(session, (cmd == IPADDRESS_DEL) ? IPSET_CMD_DEL : IPSET_CMD_ADD, set, &addr, 0, 0, ifname); } +#ifdef _HAVE_VRRP_VMAC_ +void ipset_entry_nd(void* vsession, int cmd, const interface_t* ifp) +{ + struct ipset_session *session = vsession; + ip_address_t addr = { .ifa.ifa_family = AF_INET6, .u.sin6_addr = ifp->base_ifp->sin6_addr }; + + + do_ipset_cmd(session, (cmd == IPADDRESS_DEL) ? IPSET_CMD_DEL : IPSET_CMD_ADD, global_data->vrrp_ipset_vmac_nd, &addr, -1, 0, ifp->ifname); +} +#endif + void set_default_ipsets(void) { @@ -439,6 +469,9 @@ set_default_ipsets(void) global_data->vrrp_ipset_address_iface6 = STRDUP(DEFAULT_IPSET_NAME "_if6"); global_data->vrrp_ipset_igmp = STRDUP(DEFAULT_IPSET_NAME "_igmp"); global_data->vrrp_ipset_mld = STRDUP(DEFAULT_IPSET_NAME "_mld"); +#ifdef _HAVE_VRRP_VMAC_ + global_data->vrrp_ipset_vmac_nd = STRDUP(DEFAULT_IPSET_NAME "_nd"); +#endif } void @@ -450,4 +483,7 @@ disable_ipsets(void) FREE_CONST_PTR(global_data->vrrp_ipset_address_iface6); FREE_CONST_PTR(global_data->vrrp_ipset_igmp); FREE_CONST_PTR(global_data->vrrp_ipset_mld); +#ifdef _HAVE_VRRP_VMAC_ + FREE_CONST_PTR(global_data->vrrp_ipset_vmac_nd); +#endif } diff --git a/keepalived/vrrp/vrrp_iptables.c b/keepalived/vrrp/vrrp_iptables.c index 0eef2bc8da..842e5645fb 100644 --- a/keepalived/vrrp/vrrp_iptables.c +++ b/keepalived/vrrp/vrrp_iptables.c @@ -216,6 +216,9 @@ add_del_igmp_rules(struct ipt_handle *h, int cmd, uint8_t family) if (h->h6 || (h->h6 = ip6tables_open("filter"))) { ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_mld, IPPROTO_ICMPV6, ICMPV6_MLD2_REPORT, cmd, false); +#ifdef _HAVE_VRRP_VMAC_ + ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_ONE_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_vmac_nd, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); +#endif h->updated_v6 = true; } } @@ -341,33 +344,33 @@ handle_iptable_rule_to_vip(ip_address_t *ipaddress, int cmd, struct ipt_handle * IN6_IS_ADDR_LINKLOCAL(&ipaddress->u.sin6_addr)) ifname = ipaddress->ifp->ifname; - iptables_entry(h, family, global_data->vrrp_iptables_inchain, 0, - XTC_LABEL_DROP, NULL, ipaddress, ifname, NULL, - IPPROTO_NONE, 0, cmd, 0, force); - - if (global_data->vrrp_iptables_outchain) - iptables_entry(h, family, global_data->vrrp_iptables_outchain, 0, - XTC_LABEL_DROP, ipaddress, NULL, NULL, ifname, - IPPROTO_NONE, 0, cmd, 0, force); - if (family == AF_INET6 && global_data->vrrp_iptables_inchain) { if (global_data->vrrp_iptables_outchain) { - iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, 0, + iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, APPEND_RULE, XTC_LABEL_ACCEPT, ipaddress, NULL, NULL, ifname, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, 0, force); - iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, 1, + iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, APPEND_RULE, XTC_LABEL_ACCEPT, ipaddress, NULL, NULL, ifname, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, 0, force); } - iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, 0, + iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, APPEND_RULE, XTC_LABEL_ACCEPT, NULL, ipaddress, ifname, NULL, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, 0, force); - iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, 1, + iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, APPEND_RULE, XTC_LABEL_ACCEPT, NULL, ipaddress, ifname, NULL, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, 0, force); } + if (global_data->vrrp_iptables_outchain) + iptables_entry(h, family, global_data->vrrp_iptables_outchain, APPEND_RULE, + XTC_LABEL_DROP, ipaddress, NULL, NULL, ifname, + IPPROTO_NONE, 0, cmd, 0, force); + + iptables_entry(h, family, global_data->vrrp_iptables_inchain, APPEND_RULE, + XTC_LABEL_DROP, NULL, ipaddress, ifname, NULL, + IPPROTO_NONE, 0, cmd, 0, force); + ipaddress->iptable_rule_set = (cmd != IPADDRESS_DEL); } @@ -578,12 +581,58 @@ handle_iptable_rule_for_igmp(const char *ifname, int cmd, int family, struct ipt } #endif - iptables_entry(h, family, global_data->vrrp_iptables_outchain, APPEND_RULE, + iptables_entry(h, family, global_data->vrrp_iptables_outchain, 0, XTC_LABEL_DROP, NULL, NULL, NULL, ifname, family == AF_INET ? IPPROTO_IGMP : IPPROTO_ICMPV6, family == AF_INET ? 0 : ICMPV6_MLD2_REPORT, cmd, 0, false); } +static void +handle_iptable_rule_for_nd(const interface_t *ifp, int cmd, struct ipt_handle *h) +{ + ip_address_t addr = { .ifa.ifa_family = AF_INET6, .u.sin6_addr = ifp->base_ifp->sin6_addr }; + + if (!global_data->vrrp_iptables_outchain || + igmp_setup[1] == INIT_FAILED) + return; + + if (igmp_setup[1] == NOT_INIT) { + if (setup[1] == NOT_INIT) + iptables_init(AF_INET6); + + if (setup[1] == INIT_FAILED) { + igmp_setup[1] = INIT_FAILED; + return; + } + +#ifdef _HAVE_LIBIPSET_ + if (global_data->using_ipsets) { + add_del_igmp_sets(h, IPADDRESS_ADD, AF_INET6); + add_del_igmp_rules(h, IPADDRESS_ADD, AF_INET6); + } +#endif + + igmp_setup[1] = INIT_SUCCESS; + } + +#ifdef _HAVE_LIBIPSET_ + if (global_data->using_ipsets) + { + if (!h->session) + h->session = ipset_session_start(); + + ipset_entry_nd(h->session, cmd, ifp); + + return; + } +#endif + + iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, 0, + XTC_LABEL_DROP, &addr, NULL, NULL, ifp->ifname, + IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, + cmd, 0, false); +} + static void iptables_update_vmac(const interface_t *ifp, int family, bool other_family, int cmd) { @@ -598,6 +647,10 @@ iptables_update_vmac(const interface_t *ifp, int family, bool other_family, int if (other_family) handle_iptable_rule_for_igmp(ifp->ifname, cmd, family == AF_INET ? AF_INET6 : AF_INET, h); + + if (family == AF_INET6) + handle_iptable_rule_for_nd(ifp, cmd, h); + res = iptables_close(h); } while (res == EAGAIN && ++tries < IPTABLES_MAX_TRIES); } diff --git a/keepalived/vrrp/vrrp_nftables.c b/keepalived/vrrp/vrrp_nftables.c index 822476aca5..12200d91f5 100644 --- a/keepalived/vrrp/vrrp_nftables.c +++ b/keepalived/vrrp/vrrp_nftables.c @@ -82,6 +82,7 @@ static const char vmac_set_name[] = "vmac_set"; static const char vmac_set_name[] = "vmac_set"; static const char *vmac_map_name = vmac_set_name; #endif +static const char parent_link_local_set_name[] = "parent_link_local"; #if HAVE_DECL_NFT_META_OIFKIND static const char macvlan[16] = "macvlan"; #endif @@ -558,6 +559,85 @@ static struct nftnl_rule *setup_rule_simple(uint8_t family, const char *table, } #endif +#ifdef _HAVE_VRRP_VMAC_ +static void +setup_parent_link_local(struct mnl_nlmsg_batch *batch) +{ + struct nlmsghdr *nlh; + struct nftnl_set *s; + struct nftnl_rule *r; + int type_for_if; + uint8_t protocol = IPPROTO_ICMPV6; + struct icmp6_hdr icmp6; + + if (!ifname_type) + ifname_type = set_nf_ifname_type(); + + type_for_if = global_data->vrrp_nf_ifindex ? NFT_TYPE_IFINDEX : ifname_type; + +#ifdef _HAVE_VRRP_VMAC_ + /* nft add set ip6 keepalived parent_link_local { type ipv6_addr . iface_index/name } */ + s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, parent_link_local_set_name, (NFT_TYPE_IP6ADDR << NFT_TYPE_BITS) | type_for_if, 0, 0); +#endif + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWSET, NFPROTO_IPV6, + NLM_F_CREATE|NLM_F_ACK, seq++); + + nftnl_set_nlmsg_build_payload(nlh, s); + nftnl_set_free(s); + my_mnl_nlmsg_batch_next(batch); + + /* nft add rule ip6 keepalived out icmpv6 type nd-neighbor-advert [meta oifkind macvlan] ip6 saddr . oif @parent_link_local */ + r = nftnl_rule_alloc(); + if (r == NULL) { + log_message(LOG_INFO, "OOM error - %d", errno); + return; + } + + nftnl_rule_set_str(r, NFTNL_RULE_TABLE, global_data->vrrp_nf_table_name); + nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, "out"); + nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV6); + +#if HAVE_DECL_NFT_META_L4PROTO + add_meta(r, NFT_META_L4PROTO, NFT_REG_1); /* From Linux 3.14 */ +#else + add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, + offsetof(struct ip6_hdr, ip6_nxt), sizeof(((struct ip6_hdr *)NULL)->ip6_nxt)); +#endif + add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &protocol, sizeof(protocol)); + add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, + offsetof(struct icmp6_hdr, icmp6_type), sizeof(icmp6.icmp6_type)); + icmp6.icmp6_type = ND_NEIGHBOR_ADVERT; + add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); + +#if HAVE_DECL_NFT_META_OIFKIND + add_meta(r, NFT_META_OIFKIND, NFT_REG_2); + add_cmp(r, NFT_REG_2, NFT_CMP_EQ, &macvlan, sizeof(macvlan)); +#endif + + add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, + offsetof(struct ip6_hdr, ip6_src), sizeof(struct in6_addr)); + + add_meta(r, global_data->vrrp_nf_ifindex ? NFT_META_OIF : NFT_META_OIFNAME, NFT_REG_2); + + add_lookup(r, NFT_REG_1, NO_REG, parent_link_local_set_name, 0, false); + + add_counter(r); + + add_immediate_verdict(r, NF_DROP, NULL); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWRULE, + nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), + NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); + + nftnl_rule_nlmsg_build_payload(nlh, r); + nftnl_rule_free(r); + my_mnl_nlmsg_batch_next(batch); +} +#endif + static void setup_link_local_checks(struct mnl_nlmsg_batch *batch, bool concat_ifname) { @@ -575,8 +655,8 @@ setup_link_local_checks(struct mnl_nlmsg_batch *batch, bool concat_ifname) s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, set_name, (NFT_TYPE_IP6ADDR << NFT_TYPE_BITS) | type_for_if, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), - NFT_MSG_NEWSET, NFPROTO_IPV6, - NLM_F_CREATE|NLM_F_ACK, seq++); + NFT_MSG_NEWSET, NFPROTO_IPV6, + NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, s); nftnl_set_free(s); @@ -810,6 +890,10 @@ nft_setup_ipv6(struct mnl_nlmsg_batch *batch) nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); +#ifdef _HAVE_VRRP_VMAC_ + setup_parent_link_local(batch); +#endif + ipv6_table_setup = true; } @@ -903,7 +987,7 @@ nft_setup_ipv6_vips(struct mnl_nlmsg_batch *batch) nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); - /* nft add rule ip6 keepalived in icmpv6 @neighbor-discovery accept */ + /* nft add rule ip6 keepalived in type icmpv6 @neighbor-discovery accept */ r = setup_rule_icmpv6(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "in", NULL, "neighbor-discovery", 0, NF_ACCEPT, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), @@ -926,7 +1010,7 @@ nft_setup_ipv6_vips(struct mnl_nlmsg_batch *batch) nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); - /* nft add rule ip6 keepalived in icmpv6 @neighbor-discovery accept */ + /* nft add rule ip6 keepalived in icmpv6 type @neighbor-discovery accept */ r = setup_rule_icmpv6(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "out", NULL, "neighbor-discovery", 0, NF_ACCEPT, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), @@ -1009,7 +1093,7 @@ nft_update_ipv6_address(struct mnl_nlmsg_batch *batch, ip_address_t *addr, bool struct nftnl_set **set_global, struct nftnl_set **set_ll, struct nftnl_set **set_ll_ifname) { struct nftnl_set_elem *e; - uint32_t data_buf[sizeof(struct in6_addr) + IFNAMSIZ]; + uint32_t data_buf[(sizeof(struct in6_addr) + IFNAMSIZ + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; struct nftnl_set **s; const char *set_name; bool use_link_name = false; @@ -1410,10 +1494,52 @@ nft_update_vmac_element(struct mnl_nlmsg_batch *batch, struct nftnl_set *s, ifin my_mnl_nlmsg_batch_next(batch); } +static void +nft_update_parent_link_local_element(struct mnl_nlmsg_batch *batch, struct nftnl_set *s, const interface_t *ifp, + int cmd, uint8_t nfproto) +{ + struct nlmsghdr *nlh; + struct nftnl_set_elem *e; + uint16_t type = cmd == NFT_MSG_NEWSETELEM ? NLM_F_CREATE | NLM_F_ACK : NLM_F_ACK; + uint32_t data_buf[(sizeof(struct in6_addr) + IFNAMSIZ + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; + uint32_t len; + + /* nft add element ip keepalived parent_link_local { fe80::b89a:21ff:fee1:f794, "eth0" } */ + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + cmd, nfproto, + type, seq++); + data_buf[0] = ifp->base_ifp->sin6_addr.s6_addr32[0]; + data_buf[1] = ifp->base_ifp->sin6_addr.s6_addr32[1]; + data_buf[2] = ifp->base_ifp->sin6_addr.s6_addr32[2]; + data_buf[3] = ifp->base_ifp->sin6_addr.s6_addr32[3]; + len = sizeof(struct in6_addr); + + if (!(e = nftnl_set_elem_alloc())) { + log_message(LOG_INFO, "OOM error - %d", errno); + return; + } + + if (global_data->vrrp_nf_ifindex) { + data_buf[4] = ifp->ifindex; + len += sizeof(data_buf[4]); + } else { + memset(&data_buf[4], 0, IFNAMSIZ); + memcpy(&data_buf[4], ifp->ifname, strlen(ifp->ifname)); + len += IFNAMSIZ; + } + + nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &data_buf, len); + nftnl_set_elem_add(s, e); + + nftnl_set_elems_nlmsg_build_payload(nlh, s); + my_mnl_nlmsg_batch_next(batch); +} + static void nft_update_vmac_family(struct mnl_nlmsg_batch *batch, const interface_t *ifp, uint8_t nfproto, int cmd) { struct nftnl_set *s, *s1 = NULL; + struct nftnl_set *s2; if ((nfproto == NFPROTO_IPV4 && !ipv4_igmp_setup) || (nfproto == NFPROTO_IPV6 && !ipv6_igmp_setup)) @@ -1443,10 +1569,23 @@ nft_update_vmac_family(struct mnl_nlmsg_batch *batch, const interface_t *ifp, ui } nft_update_vmac_element(batch, s, ifp->ifindex, ifp->base_ifp->ifindex, cmd, nfproto); + if (nfproto == NFPROTO_IPV6) { #if HAVE_DECL_NFTA_DUP_MAX - if (nfproto == NFPROTO_IPV6) nft_update_vmac_element(batch, s1, ifp->ifindex, 0, cmd, nfproto); #endif + s2 = nftnl_set_alloc(); + if (s2 == NULL) { + log_message(LOG_INFO, "OOM error - %d", errno); + return; + } + + nftnl_set_set_str(s2, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); + nftnl_set_set_str(s2, NFTNL_SET_NAME, parent_link_local_set_name); + + nft_update_parent_link_local_element(batch, s2, ifp, cmd, nfproto); + + nftnl_set_free(s2); + } nftnl_set_free(s); #if HAVE_DECL_NFTA_DUP_MAX