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

Add options for setting interface group of VMACs and ipvlans #2391

Merged
merged 7 commits into from
Mar 1, 2024
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
2 changes: 1 addition & 1 deletion INSTALL
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ To build using clang/llvm
Alpine Linux
------------
The following libraries need to be installed:
iptables-dev ipset-dev libnl3-dev musl-dev libnftnl-dev and openssl-dev or libressl-dev
linux-headers iptables-dev ipset-dev libnl3-dev musl-dev libnftnl-dev and openssl-dev or libressl-dev
For magic file identification support:
file-dev
For SNMP support:
Expand Down
13 changes: 10 additions & 3 deletions doc/man/man5/keepalived.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,11 @@ The syntax for vrrp_instance is :
# interface it will force a netlink message to be sent for the base interface
# since the kernel does not send one, even if the promiscuity of the base
# interface has been updated.
\fBuse_vmac \fR[<VMAC_INTERFACE_NAME>] [MAC_ADDRESS] [netlink_notify_msg]
# By default the VMAC is created in the same link group as the parent interface.
# Specifying group GROUP_ID (where GROUP_ID is either a valid group number, or a
# name in /etc/iproute2/group) will create the interface in the specied group.
# The name option can be specified if you want to use an interface name "group".
\fBuse_vmac \fR[[name] <VMAC_INTERFACE_NAME>] [MAC_ADDRESS] [netlink_notify_msg] [group GROUP_ID]

# use_vmac_addr is used to create VMAC (macvlan) interfaces for
# each interface that is used by a VIP or eVIP where the interface
Expand All @@ -1649,12 +1653,15 @@ The syntax for vrrp_instance is :
# For IPv4 instances, an IP address is required, for IPv6
# the address is optional, in which case the link local
# address will be used.
# The mode flags default to bridge. NOTE: the mode flags must be the
# The mode flags defaults to bridge. NOTE: the mode flags must be the
# same for all ipvlans on the same underlying interface.
# It is safer to configure an interface name, in case keepalived crashes
# and restarts, in which case it can more reliably find a previously
# created interface.
\fBuse_ipvlan \fR[<INTERFACE_NAME>] [IP_ADDRESS] [bridge|private|vepa]
# The name option can be specified if you want to use a name that would cause
# a parsing error (e.g. "bridge").
# For a description of the group option, see use_vmac.
\fBuse_ipvlan \fR[[name] <INTERFACE_NAME>] [IP_ADDRESS] [bridge|private|vepa] [group GROUP_ID]

# force instance to use IPv6 (this option is deprecated since
# the virtual ip addresses determine whether IPv4 or IPv6 is used).
Expand Down
2 changes: 2 additions & 0 deletions keepalived/include/vrrp.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ enum vrrp_flags_bits {
VRRP_VMAC_XMITBASE_BIT,
VRRP_VMAC_ADDR_BIT,
VRRP_VMAC_NETLINK_NOTIFY,
VRRP_VMAC_GROUP,
#ifdef _HAVE_VRRP_IPVLAN_
VRRP_IPVLAN_BIT,
#endif
Expand Down Expand Up @@ -260,6 +261,7 @@ typedef struct _vrrp_t {
#ifdef _HAVE_VRRP_VMAC_
char vmac_ifname[IFNAMSIZ]; /* Name of VRRP VMAC interface */
u_char ll_addr[ETH_ALEN]; /* Override MAC address */
uint32_t vmac_group; /* interface group for VMAC/ipvlan */
#ifdef _HAVE_VRRP_IPVLAN_
struct _ip_address *ipvlan_addr; /* Address to configure on an ipvlan interface */
int ipvlan_type; /* Bridge, private or VEPA mode */
Expand Down
18 changes: 14 additions & 4 deletions keepalived/vrrp/vrrp_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,10 +648,6 @@ dump_vrrp(FILE *fp, const vrrp_t *vrrp)
vrrp->ll_addr[0], vrrp->ll_addr[1], vrrp->ll_addr[2], vrrp->ll_addr[3], vrrp->ll_addr[4], vrrp->ll_addr[5],
__test_bit(VRRP_VMAC_MAC_USE_VRID, &vrrp->flags) ? " (using VRID)" : "");
}
if (__test_bit(VRRP_VMAC_NETLINK_NOTIFY, &vrrp->flags))
conf_write(fp, " Force netlink update for base interface");
if (__test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags))
conf_write(fp, " Use VMAC for VIPs on other interfaces");
#ifdef _HAVE_VRRP_IPVLAN_
else if (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags))
conf_write(fp, " Use IPVLAN, i/f %s, is_up = %s%s%s, type %s",
Expand All @@ -666,6 +662,20 @@ dump_vrrp(FILE *fp, const vrrp_t *vrrp)
#endif
);
#endif

/* The following two flags should only be set on VMACs, but
* we check them for any interface type, just incase ... */
if (__test_bit(VRRP_VMAC_NETLINK_NOTIFY, &vrrp->flags))
conf_write(fp, " Force netlink update for base interface");
if (__test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags))
conf_write(fp, " Use VMAC for VIPs on other interfaces");

/* The following should only be specified for VMACs and ipvlans */
if (__test_bit(VRRP_VMAC_GROUP, &vrrp->flags))
conf_write(fp, " Interface group %u", vrrp->vmac_group);
else if (vrrp->ifp->base_ifp->group)
conf_write(fp, " Interface group %u (copied from parent)", vrrp->ifp->base_ifp->group);

if (vrrp->ifp && vrrp->ifp->is_ours) {
conf_write(fp, " Interface = %s, %s on %s%s", IF_NAME(vrrp->ifp),
__test_bit(VRRP_VMAC_BIT, &vrrp->flags) ? "vmac" : "ipvlan",
Expand Down
99 changes: 65 additions & 34 deletions keepalived/vrrp/vrrp_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include "global_data.h"
#include "global_parser.h"

#include "rttables.h"

#include "vrrp_data.h"
#include "vrrp_ipaddress.h"
#include "vrrp_sync.h"
Expand Down Expand Up @@ -514,7 +516,7 @@ vrrp_vmac_handler(const vector_t *strvec)
__set_bit(VRRP_VMAC_BIT, &current_vrrp->flags);

/* Ifname and MAC address can be specified */
for (i = 1; i < vector_size(strvec) && i <= 2; i++) {
for (i = 1; i < vector_size(strvec); i++) {
if (strchr(strvec_slot(strvec, i), ':')) {
/* It's a MAC address - interface names cannot include a ':' */
if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &current_vrrp->flags)) {
Expand Down Expand Up @@ -560,49 +562,65 @@ vrrp_vmac_handler(const vector_t *strvec)
report_config_error(CONFIG_GENERAL_ERROR, "VMAC MAC address not allowed to be RFC5798 address (%s) - ignoring", strvec_slot(strvec, i));
else
__set_bit(VRRP_VMAC_MAC_SPECIFIED, &current_vrrp->flags);
} else {
name = strvec_slot(strvec, i);

if (current_vrrp->vmac_ifname[0]) {
report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name already specified");
continue;
}
continue;
}

/* The string "netlink_notify_msg" needs to be longer than IFNAMSIZ
* so that it cannot be a valid interface name. */
if (!strcmp(name, "netlink_notify_msg")) {
__set_bit(VRRP_VMAC_NETLINK_NOTIFY, &current_vrrp->flags);
continue;
}
if (!strcmp(strvec_slot(strvec, i), "netlink_notify_msg")) {
__set_bit(VRRP_VMAC_NETLINK_NOTIFY, &current_vrrp->flags);
continue;
}

if (!dev_name_valid(name)) {
report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name '%s' too long or invalid characters - ignoring", name);
if (!strcmp(strvec_slot(strvec, i), "group")) {
uint32_t group;
if (!find_rttables_group(strvec_slot(strvec, ++i), &group)) {
report_config_error(CONFIG_GENERAL_ERROR, "VMAC group %s not found", strvec_slot(strvec, i));
continue;
}
__set_bit(VRRP_VMAC_GROUP, &current_vrrp->flags);
current_vrrp->vmac_group = group;
continue;
}

/* Check another vrrp instance isn't using this name */
list_for_each_entry(ovrrp, &vrrp_data->vrrp, e_list) {
if (!strcmp(name, ovrrp->vmac_ifname)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRP instance %s is already using %s - ignoring name", current_vrrp->iname, ovrrp->iname, name);
name = NULL;
break;
}
}
if (!strcmp(strvec_slot(strvec, i), "name")) {
/* Skip over "name" */
i++;
}

if (!name)
continue;
if (current_vrrp->vmac_ifname[0]) {
report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name already specified");
continue;
}

strcpy(current_vrrp->vmac_ifname, name);
name = strvec_slot(strvec, i);

/* Check if the interface exists and is a macvlan we can use */
if ((ifp = if_get_by_ifname(current_vrrp->vmac_ifname, IF_NO_CREATE)) &&
(ifp->if_type != IF_TYPE_MACVLAN ||
ifp->vmac_type != MACVLAN_MODE_PRIVATE)) {
/* ??? also check ADDR_GEN_MODE and VRF enslavement matches parent */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) interface %s already exists and is not a private macvlan; ignoring vmac if_name", current_vrrp->iname, current_vrrp->vmac_ifname);
current_vrrp->vmac_ifname[0] = '\0';
if (!dev_name_valid(name)) {
report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name '%s' too long or invalid characters - ignoring", name);
continue;
}

/* Check another vrrp instance isn't using this name */
list_for_each_entry(ovrrp, &vrrp_data->vrrp, e_list) {
if (!strcmp(name, ovrrp->vmac_ifname)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRP instance %s is already using %s - ignoring name", current_vrrp->iname, ovrrp->iname, name);
name = NULL;
break;
}
}

if (!name)
continue;

strcpy(current_vrrp->vmac_ifname, name);

/* Check if the interface exists and is a macvlan we can use */
if ((ifp = if_get_by_ifname(current_vrrp->vmac_ifname, IF_NO_CREATE)) &&
(ifp->if_type != IF_TYPE_MACVLAN ||
ifp->vmac_type != MACVLAN_MODE_PRIVATE)) {
/* ??? also check ADDR_GEN_MODE and VRF enslavement matches parent */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) interface %s already exists and is not a private macvlan; ignoring vmac if_name", current_vrrp->iname, current_vrrp->vmac_ifname);
current_vrrp->vmac_ifname[0] = '\0';
}
}
}

Expand Down Expand Up @@ -677,7 +695,20 @@ vrrp_ipvlan_handler(const vector_t *strvec)
continue;
}

if (check_valid_ipaddress(strvec_slot(strvec, i), true)) {
if (!strcmp(strvec_slot(strvec, i), "group")) {
uint32_t group;
if (!find_rttables_group(strvec_slot(strvec, ++i), &group)) {
report_config_error(CONFIG_GENERAL_ERROR, "ipvlan group %s not found", strvec_slot(strvec, i));
continue;
}
__set_bit(VRRP_VMAC_GROUP, &current_vrrp->flags);
current_vrrp->vmac_group = group;
continue;
}

if (!strcmp(strvec_slot(strvec, i), "name")) {
i++;
} else if (check_valid_ipaddress(strvec_slot(strvec, i), true)) {
parse_ipaddress(&addr, strvec_slot(strvec, i), true);
if (current_vrrp->ipvlan_addr) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan address already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i));
Expand Down
17 changes: 11 additions & 6 deletions keepalived/vrrp/vrrp_vmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ netlink_link_up(vrrp_t *vrrp)
static void
netlink_link_group(interface_t *base_ifp)
{
uint32_t group = base_ifp->group;
struct {
struct nlmsghdr n;
struct ifinfomsg ifi;
Expand All @@ -246,7 +245,7 @@ netlink_link_group(interface_t *base_ifp)
req.ifi.ifi_family = AF_UNSPEC;
req.ifi.ifi_index = (int)IF_INDEX(base_ifp);

addattr_l(&req.n, sizeof(req), IFLA_GROUP, &group, sizeof(group));
addattr32(&req.n, sizeof(req), IFLA_GROUP, base_ifp->group);
netlink_talk(&nl_cmd, &req.n);
}

Expand Down Expand Up @@ -277,7 +276,6 @@ netlink_link_add_vmac(vrrp_t *vrrp, const interface_t *old_interface)
struct rtattr *linkinfo;
struct rtattr *data;
interface_t *ifp;
uint32_t group;
bool create_interface = true;
struct {
struct nlmsghdr n;
Expand Down Expand Up @@ -393,8 +391,9 @@ netlink_link_add_vmac(vrrp_t *vrrp, const interface_t *old_interface)
* (iptables devgroup or nftables iifgroup, oifgroup) to continue
* working regardless of the use_vmac setting.
*/
group = vrrp->configured_ifp->base_ifp->group;
addattr_l(&req.n, sizeof(req), IFLA_GROUP, &group, sizeof(group));
addattr32(&req.n, sizeof(req), IFLA_GROUP,
__test_bit(VRRP_VMAC_GROUP, &vrrp->flags) ? vrrp->vmac_group
: vrrp->configured_ifp->base_ifp->group);
addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, if_ll_addr, ETH_ALEN);

#ifdef _HAVE_VRF_
Expand Down Expand Up @@ -619,9 +618,15 @@ netlink_link_add_ipvlan(vrrp_t *vrrp)
/* ipvlan settings */

/* Note: if the underlying interface is a ipvlan, then the kernel will configure the
* interface only the underlying interface of the ipvlan */
* interface only the underlying interface of the ipvlan.
* We copy the group from the base interface to allow firewall rules
* (iptables devgroup or nftables iifgroup, oifgroup) to continue
* working regardless of the use_vmac setting. */
addattr32(&req.n, sizeof(req), IFLA_LINK, vrrp->configured_ifp->ifindex);
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, vrrp->vmac_ifname, strlen(vrrp->vmac_ifname));
addattr32(&req.n, sizeof(req), IFLA_GROUP,
__test_bit(VRRP_VMAC_GROUP, &vrrp->flags) ? vrrp->vmac_group
: vrrp->configured_ifp->base_ifp->group);
linkinfo = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n));
addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (const void *)ipvlan_ll_kind, strlen(ipvlan_ll_kind));
Expand Down
4 changes: 1 addition & 3 deletions lib/rttables.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,11 @@ find_rttables_dsfield(const char *name, uint8_t *id)
return ret;
}

#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
bool
find_rttables_group(const char *name, uint32_t *id)
{
return find_entry(name, id, &rt_groups, RT_GROUPS_FILE, NULL, INT32_MAX);
}
#endif

bool
find_rttables_realms(const char *name, uint32_t *id)
Expand Down Expand Up @@ -386,7 +384,7 @@ get_entry(unsigned int id, list_head_t *l, const char* file_name, const rt_entry
return ret_buf;
}

#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP && defined _WITH_SNMP_VRRP_
const char *
get_rttables_group(uint32_t id)
{
Expand Down
8 changes: 1 addition & 7 deletions lib/rttables.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,18 @@
#include <stdint.h>

extern void clear_rt_names(void);
#ifdef _WITH_VRRP_
extern bool find_rttables_table(const char *, uint32_t *);
extern bool find_rttables_dsfield(const char *, uint8_t *);
extern bool find_rttables_realms(const char *, uint32_t *);
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
extern bool find_rttables_group(const char *, uint32_t *);
#endif
extern bool find_rttables_proto(const char *, uint8_t *);
extern bool find_rttables_rtntype(const char *, uint8_t *);
#endif
extern bool find_rttables_scope(const char *, uint8_t *);

extern const char *get_rttables_scope(uint32_t);
#ifdef _WITH_VRRP_
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP && defined _WITH_SNMP_VRRP_
extern const char *get_rttables_group(uint32_t);
#endif
extern const char *get_rttables_rtntype(uint8_t);
#endif

#endif
Loading