Skip to content
Permalink
Browse files

Add support for use_ipvlan (use an ipvlan i/f similar to use_vmac)

Issue #1170 identified that use_vmac didn't work with systemd-networkd
since systemd-networkd was removing IP addresses created by keepalived
(and any other application). It was discovered that systemd-networkd
did not remove IP addresses from ipvlans.

This commit adds support for ipvlans, but to work around the problem,
and because it might have other uses.

Systemd commit - systemd/systemd#12511 has added
configuration options to stop systemd-networkd removing IP addresses
added by other applications, but it is not merged yet, and it will be a
while before all the distros merge it.

Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
  • Loading branch information...
pqarmitage committed May 13, 2019
1 parent 4e60fea commit 897690b25df0f444142ec37fdea2ce9a77dac493
@@ -1794,6 +1794,26 @@ if test "$enable_vrrp" != no; then
fi fi
CPPFLAGS="$SAV_CPPFLAGS" CPPFLAGS="$SAV_CPPFLAGS"


dnl ----[ Checks for kernel IPVLAN support ]----
SAV_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $kernelinc"
IPVLAN_SUPPORT=Yes
dnl -- Since Linux 3.19
AC_CHECK_DECLS([
IFLA_IPVLAN_MODE], [],
[
IPVLAN_SUPPORT=No
break
], [[
#include <sys/socket.h>
#include <linux/if_link.h>
]])
if test $IPVLAN_SUPPORT = Yes; then
AC_DEFINE([_HAVE_VRRP_IPVLAN_], [ 1 ], [Define to 1 if have IP VLAN support])
add_system_opt([VRRP_IPVLAN])
fi
CPPFLAGS="$SAV_CPPFLAGS"

dnl ----[ Check for IFLA_LINK_NETNSID support ]---- since Linux v4.0 dnl ----[ Check for IFLA_LINK_NETNSID support ]---- since Linux v4.0
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <linux/if_link.h> #include <linux/if_link.h>
@@ -1196,6 +1196,18 @@ The syntax for vrrp_instance is :
# VMAC interface # VMAC interface
\fBvmac_xmit_base\fR \fBvmac_xmit_base\fR


# Use IPVLAN interface. keepalived will create a mode L2
# ipvlan interface on top of the specified interface.
# 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 private. 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]

# force instance to use IPv6 (this option is deprecated since # force instance to use IPv6 (this option is deprecated since
# the virtual ip addresses determine whether IPv4 or IPv6 is used). # the virtual ip addresses determine whether IPv4 or IPv6 is used).
\fBnative_ipv6\fR \fBnative_ipv6\fR
@@ -743,6 +743,12 @@ addattr8(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint8_t data)
} }
#endif #endif


int
addattr16(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint16_t data)
{
return addattr_l(n, maxlen, type, &data, sizeof data);
}

int int
addattr32(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint32_t data) addattr32(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint32_t data)
{ {
@@ -1037,7 +1043,7 @@ netlink_if_address_filter(__attribute__((unused)) struct sockaddr_nl *snl, struc
addr_chg = true; addr_chg = true;
} }
#if defined _HAVE_VRRP_VMAC_ && !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE #if defined _HAVE_VRRP_VMAC_ && !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE
else if (ifp->is_ours && ifp->vmac_type) { else if (ifp->is_ours && ifp->if_type == IF_TYPE_MACVLAN) {
/* We already have an address; is this an auto generated link local address? /* We already have an address; is this an auto generated link local address?
* For some reason if we recreate the VMAC when the underlying interface is * For some reason if we recreate the VMAC when the underlying interface is
* recreated, deleting the autogenerated address doesn't get rid of the address */ * recreated, deleting the autogenerated address doesn't get rid of the address */
@@ -1081,10 +1087,10 @@ netlink_if_address_filter(__attribute__((unused)) struct sockaddr_nl *snl, struc
try_up_instance(vrrp, false); try_up_instance(vrrp, false);
} }
#ifdef _HAVE_VRRP_VMAC_ #ifdef _HAVE_VRRP_VMAC_
// If IPv6 link local and vmac doesn't have an address, add it to the vmac /* If IPv6 link local and vmac doesn't have an address, add it to the vmac */
else if (vrrp->family == AF_INET6 && else if (vrrp->family == AF_INET6 &&
ifp == vrrp->ifp->base_ifp && ifp == vrrp->ifp->base_ifp &&
vrrp->ifp->vmac_type && IS_VLAN(vrrp->ifp) &&
!__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->vmac_flags) && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->vmac_flags) &&
vrrp->num_script_if_fault && vrrp->num_script_if_fault &&
vrrp->family == ifa->ifa_family && vrrp->family == ifa->ifa_family &&
@@ -1397,8 +1403,8 @@ netlink_parse_info(int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
if (netlink_error_ignore != -err->error) if (netlink_error_ignore != -err->error)
#endif #endif
log_message(LOG_INFO, log_message(LOG_INFO,
"Netlink: error: %s, type=%s(%u), seq=%u, pid=%d", "Netlink: error: %s(%d), type=%s(%u), seq=%u, pid=%d",
strerror(-err->error), strerror(-err->error), -err->error,
get_nl_msg_type(err->msg.nlmsg_type), err->msg.nlmsg_type, get_nl_msg_type(err->msg.nlmsg_type), err->msg.nlmsg_type,
err->msg.nlmsg_seq, err->msg.nlmsg_pid); err->msg.nlmsg_seq, err->msg.nlmsg_pid);


@@ -1735,8 +1741,12 @@ netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg
char *name; char *name;
#ifdef _HAVE_VRRP_VMAC_ #ifdef _HAVE_VRRP_VMAC_
struct rtattr* linkinfo[IFLA_INFO_MAX+1]; struct rtattr* linkinfo[IFLA_INFO_MAX+1];
struct rtattr* linkattr[IFLA_MACVLAN_MAX+1]; #ifdef _HAVE_VRRP_IPVLAN_
bool is_macvlan = false; struct rtattr* linkattr[max(max(IFLA_MACVLAN_MAX, IFLA_IPVLAN_MAX), IFLA_VRF_MAX) + 1];
#else
struct rtattr* linkattr[max(IFLA_MACVLAN_MAX, IFLA_VRF_MAX) + 1];
#endif
bool was_vlan;
#ifdef _HAVE_VRF_ #ifdef _HAVE_VRF_
struct rtattr *vrf_attr[IFLA_VRF_MAX + 1]; struct rtattr *vrf_attr[IFLA_VRF_MAX + 1];
bool is_vrf = false; bool is_vrf = false;
@@ -1755,18 +1765,27 @@ netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg
#endif #endif


#ifdef _HAVE_VRRP_VMAC_ #ifdef _HAVE_VRRP_VMAC_
was_vlan = IS_VLAN(ifp);
if (tb[IFLA_LINKINFO]) { if (tb[IFLA_LINKINFO]) {
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);


if (linkinfo[IFLA_INFO_KIND]) { if (linkinfo[IFLA_INFO_KIND]) {
if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvlan") || if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvlan") ||
!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvtap")) { !strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvtap")) {
is_macvlan = true; ifp->if_type = IF_TYPE_MACVLAN;
parse_rtattr_nested(linkattr, IFLA_MACVLAN_MAX, linkinfo[IFLA_INFO_DATA]); parse_rtattr_nested(linkattr, IFLA_MACVLAN_MAX, linkinfo[IFLA_INFO_DATA]);
} }
#ifdef _HAVE_VRRP_IPVLAN_
else if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "ipvlan") ||
!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "ipvtap")) {
ifp->if_type = IF_TYPE_IPVLAN;
parse_rtattr_nested(linkattr, IFLA_IPVLAN_MAX, linkinfo[IFLA_INFO_DATA]);
}
#endif
#ifdef _HAVE_VRF_ #ifdef _HAVE_VRF_
else if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vrf") ) { else if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vrf") ) {
is_vrf = true; is_vrf = true;
ifp->if_type = IF_TYPE_VRF;
parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, linkinfo[IFLA_INFO_DATA]); parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, linkinfo[IFLA_INFO_DATA]);
} }
#endif #endif
@@ -1782,11 +1801,11 @@ netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg
if (!global_data->allow_if_changes && ifp->seen_interface) { if (!global_data->allow_if_changes && ifp->seen_interface) {
/* If it was a macvlan and now isn't, or vice versa, /* If it was a macvlan and now isn't, or vice versa,
* then the interface type has changed */ * then the interface type has changed */
if (is_macvlan == !ifp->vmac_type) if (IS_VLAN(ifp) != was_vlan)
return false; return false;


/* If a macvlan, check the underlying interface hasn't changed */ /* If a macvlan, check the underlying interface hasn't changed */
if (is_macvlan && if (IS_VLAN(ifp) &&
(!tb[IFLA_LINK] || ifp->base_ifp->ifindex != *(uint32_t *)RTA_DATA(tb[IFLA_LINK]))) (!tb[IFLA_LINK] || ifp->base_ifp->ifindex != *(uint32_t *)RTA_DATA(tb[IFLA_LINK])))
return false; return false;
} }
@@ -1807,10 +1826,19 @@ netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg
if (tb[IFLA_LINKINFO]) { if (tb[IFLA_LINKINFO]) {
if (linkinfo[IFLA_INFO_KIND]) { if (linkinfo[IFLA_INFO_KIND]) {
/* See if this interface is a MACVLAN */ /* See if this interface is a MACVLAN */
if (is_macvlan) { if (IS_VLAN(ifp)) {
if (linkattr[IFLA_MACVLAN_MODE] && if (((ifp->if_type == IF_TYPE_MACVLAN && linkattr[IFLA_MACVLAN_MODE])
#ifdef _HAVE_VRRP_IPVLAN_
|| (ifp->if_type == IF_TYPE_IPVLAN && linkattr[IFLA_IPVLAN_MODE])
#endif
) &&
tb[IFLA_LINK]) { tb[IFLA_LINK]) {
ifp->vmac_type = *(uint32_t*)RTA_DATA(linkattr[IFLA_MACVLAN_MODE]); if (ifp->if_type == IF_TYPE_MACVLAN)
ifp->vmac_type = *(uint32_t*)RTA_DATA(linkattr[IFLA_MACVLAN_MODE]);
#ifdef _HAVE_VRRP_IPVLAN_
else
ifp->vmac_type = *(uint32_t*)RTA_DATA(linkattr[IFLA_IPVLAN_MODE]);
#endif
ifp->base_ifindex = *(uint32_t *)RTA_DATA(tb[IFLA_LINK]); ifp->base_ifindex = *(uint32_t *)RTA_DATA(tb[IFLA_LINK]);
#ifdef HAVE_IFLA_LINK_NETNSID /* from Linux v4.0 */ #ifdef HAVE_IFLA_LINK_NETNSID /* from Linux v4.0 */
if (tb[IFLA_LINK_NETNSID]) /* Only use link details if in same network namespace */ if (tb[IFLA_LINK_NETNSID]) /* Only use link details if in same network namespace */
@@ -2088,7 +2116,8 @@ netlink_link_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlms
#ifndef _DEBUG_ #ifndef _DEBUG_
&& prog_type == PROG_TYPE_VRRP && prog_type == PROG_TYPE_VRRP
#endif #endif
) { )
{
/* Change the mac address on the interface, so we can create a new vmac */ /* Change the mac address on the interface, so we can create a new vmac */


/* Now create our VMAC again */ /* Now create our VMAC again */
@@ -73,6 +73,7 @@ extern void report_and_clear_netlink_timers(const char *);
#ifdef _WITH_VRRP_ #ifdef _WITH_VRRP_
extern int addattr_l(struct nlmsghdr *, size_t, unsigned short, const void *, size_t); extern int addattr_l(struct nlmsghdr *, size_t, unsigned short, const void *, size_t);
extern int addattr8(struct nlmsghdr *, size_t, unsigned short, uint8_t); extern int addattr8(struct nlmsghdr *, size_t, unsigned short, uint8_t);
extern int addattr16(struct nlmsghdr *, size_t, unsigned short, uint16_t);
extern int addattr32(struct nlmsghdr *, size_t, unsigned short, uint32_t); extern int addattr32(struct nlmsghdr *, size_t, unsigned short, uint32_t);
extern int addattr64(struct nlmsghdr *, size_t, unsigned short, uint64_t); extern int addattr64(struct nlmsghdr *, size_t, unsigned short, uint64_t);
extern int addattr_l2(struct nlmsghdr *, size_t, unsigned short, const void *, size_t, const void *, size_t); extern int addattr_l2(struct nlmsghdr *, size_t, unsigned short, const void *, size_t, const void *, size_t);
@@ -50,6 +50,8 @@
/* Special value for parameters when we want to know they haven't been set */ /* Special value for parameters when we want to know they haven't been set */
#define PARAMETER_UNSET UINT_MAX #define PARAMETER_UNSET UINT_MAX


struct _ip_address;

typedef struct _vrrphdr { /* rfc2338.5.1 */ typedef struct _vrrphdr { /* rfc2338.5.1 */
uint8_t vers_type; /* 0-3=type, 4-7=version */ uint8_t vers_type; /* 0-3=type, 4-7=version */
uint8_t vrid; /* virtual router id */ uint8_t vrid; /* virtual router id */
@@ -198,6 +200,10 @@ typedef struct _vrrp_t {
unsigned long vmac_flags; /* VRRP VMAC flags */ unsigned long vmac_flags; /* VRRP VMAC flags */
char vmac_ifname[IFNAMSIZ]; /* Name of VRRP VMAC interface */ char vmac_ifname[IFNAMSIZ]; /* Name of VRRP VMAC interface */
bool duplicate_vrid_fault; /* Set if we have a fault due to duplicate VRID */ bool duplicate_vrid_fault; /* Set if we have a fault due to duplicate VRID */
#ifdef _HAVE_VRRP_IPVLAN_
struct _ip_address *ipvlan_addr; /* Address to configure on an ipvlan interface */
int ipvlan_type; /* Bridge, private or VEPA mode */
#endif
#endif #endif
interface_t *configured_ifp; /* Interface the configuration says we are on */ interface_t *configured_ifp; /* Interface the configuration says we are on */
list track_ifp; /* Interface state we monitor */ list track_ifp; /* Interface state we monitor */
@@ -77,6 +77,25 @@ typedef struct _garp_delay {
int aggregation_group; /* Index of multi-interface group */ int aggregation_group; /* Index of multi-interface group */
} garp_delay_t; } garp_delay_t;


#ifdef _HAVE_VRRP_VMAC_
typedef enum {
IF_TYPE_MACVLAN = 1,
#ifdef _HAVE_VRRP_IPVLAN_
IF_TYPE_IPVLAN,
#endif
#ifdef _HAVE_VRF_
IF_TYPE_VRF,
#endif
} if_type_t;

#ifdef _HAVE_VRRP_IPVLAN_
#define IS_VLAN(IFP) ((IFP)->if_type == IF_TYPE_MACVLAN || (IFP)->if_type == IF_TYPE_IPVLAN)
#else
#define IS_VLAN(IFP) ((IFP)->if_type == IF_TYPE_MACVLAN)
#endif

#endif

/* Interface structure definition */ /* Interface structure definition */
typedef struct _interface { typedef struct _interface {
char ifname[IFNAMSIZ]; /* Interface name */ char ifname[IFNAMSIZ]; /* Interface name */
@@ -98,7 +117,8 @@ typedef struct _interface {
int lb_type; /* Interface regs selection */ int lb_type; /* Interface regs selection */
#endif #endif
#ifdef _HAVE_VRRP_VMAC_ #ifdef _HAVE_VRRP_VMAC_
int vmac_type; /* Set if interface is a VMAC interface */ if_type_t if_type; /* interface type */
int vmac_type; /* Type of macvlan or ipvlan */
ifindex_t base_ifindex; /* Only used at startup if we find vmac i/f before base i/f */ ifindex_t base_ifindex; /* Only used at startup if we find vmac i/f before base i/f */
#ifdef HAVE_IFLA_LINK_NETNSID #ifdef HAVE_IFLA_LINK_NETNSID
int base_netns_id; /* Network namespace of the parent interface */ int base_netns_id; /* Network namespace of the parent interface */
@@ -42,6 +42,9 @@ enum vrrp_vmac_bits {
VRRP_VMAC_BIT = 0, VRRP_VMAC_BIT = 0,
VRRP_VMAC_UP_BIT = 1, VRRP_VMAC_UP_BIT = 1,
VRRP_VMAC_XMITBASE_BIT = 2, VRRP_VMAC_XMITBASE_BIT = 2,
#ifdef _HAVE_VRRP_IPVLAN_
VRRP_IPVLAN_BIT = 3,
#endif
}; };


extern const char * const macvlan_ll_kind; extern const char * const macvlan_ll_kind;
@@ -55,6 +58,9 @@ extern void remove_vmac_auto_gen_addr(interface_t *, struct in6_addr *);
#endif #endif
extern bool netlink_link_add_vmac(vrrp_t *); extern bool netlink_link_add_vmac(vrrp_t *);
extern void netlink_link_del_vmac(vrrp_t *); extern void netlink_link_del_vmac(vrrp_t *);
#ifdef _HAVE_VRRP_IPVLAN_
extern bool netlink_link_add_ipvlan(vrrp_t *);
#endif
#ifdef _HAVE_VRF_ #ifdef _HAVE_VRF_
extern void update_vmac_vrfs(interface_t *); extern void update_vmac_vrfs(interface_t *);
#endif #endif

0 comments on commit 897690b

Please sign in to comment.
You can’t perform that action at this time.