Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vrrp: Stop link local VMAC address responging to neighbour solicit #2329

Merged
merged 2 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/man/man5/keepalived.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions keepalived/core/global_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Expand Down Expand Up @@ -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
}
Expand Down
33 changes: 25 additions & 8 deletions keepalived/core/global_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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_
Expand Down
1 change: 1 addition & 0 deletions keepalived/core/keepalived_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions keepalived/include/global_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Expand Down
1 change: 1 addition & 0 deletions keepalived/include/vrrp_ipset.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
48 changes: 42 additions & 6 deletions keepalived/vrrp/vrrp_ipset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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)
{
Expand All @@ -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
Expand All @@ -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
}
81 changes: 67 additions & 14 deletions keepalived/vrrp/vrrp_iptables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
}
Expand Down
Loading
Loading