Skip to content

Commit

Permalink
Merge pull request #2329 from pqarmitage/updates
Browse files Browse the repository at this point in the history
vrrp: Stop link local VMAC address responding to neighbour solicit
  • Loading branch information
pqarmitage committed Jul 25, 2023
2 parents 6968794 + 9c67c6e commit f48e6b1
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 36 deletions.
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

0 comments on commit f48e6b1

Please sign in to comment.