[PATCH 02/12] Set (and restore) interface parameters
[email protected] (Quentin Armitage)
| Newsgroups | gmane.linux.keepalived.devel |
|---|---|
| Message-ID | <[email protected]> |
In order to receive and send multicasts on the correct interfaces various parameters need to be set via the /proc/sys/net/ipv4/conf interface. This patch sets them as needed, and restores any changes on the underlying interface on exit. Signed-off-by: Quentin Armitage <[email protected]> --- configure | 55 +++++++++++++ configure.in | 18 ++++- keepalived/include/vrrp_if.h | 1 + keepalived/include/vrrp_netlink3.h | 30 +++++++ keepalived/vrrp/Makefile.in | 6 +- keepalived/vrrp/vrrp_netlink.c | 1 + keepalived/vrrp/vrrp_netlink3.c | 156 ++++++++++++++++++++++++++++++++++++ keepalived/vrrp/vrrp_vmac.c | 132 ++++++++++++++++++++++++++++++ 8 files changed, 397 insertions(+), 2 deletions(-) create mode 100644 keepalived/include/vrrp_netlink3.h create mode 100644 keepalived/vrrp/vrrp_netlink3.c diff --git a/configure b/configure index a340883..efe72af 100755 --- a/configure +++ b/configure @@ -621,6 +621,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS +USE_NL3 VRRP_SUPPORT USE_NL IPVS_SUPPORT @@ -3905,6 +3906,7 @@ else fi +USE_NL3="_WITHOUT_LIBNL_" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_socket_alloc in -lnl-3" >&5 $as_echo_n "checking for nl_socket_alloc in -lnl-3... " >&6; } if ${ac_cv_lib_nl_3_nl_socket_alloc+:} false; then : @@ -3991,6 +3993,50 @@ else fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nl_rtgen_request in -lnl-route-3" >&5 +$as_echo_n "checking for nl_rtgen_request in -lnl-route-3... " >&6; } +if ${ac_cv_lib_nl_route_3_nl_rtgen_request+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnl-route-3 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char nl_rtgen_request (); +int +main () +{ +return nl_rtgen_request (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_nl_route_3_nl_rtgen_request=yes +else + ac_cv_lib_nl_route_3_nl_rtgen_request=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nl_route_3_nl_rtgen_request" >&5 +$as_echo "$ac_cv_lib_nl_route_3_nl_rtgen_request" >&6; } +if test "x$ac_cv_lib_nl_route_3_nl_rtgen_request" = xyes; then : + + USE_NL3="_HAVE_LIBNL3_" + CFLAGS="$CFLAGS $(pkg-config --cflags libnl-route-3.0)" + LIBS="$LIBS $(pkg-config --libs libnl-route-3.0)" + +fi + else @@ -4033,6 +4079,7 @@ $as_echo "$ac_cv_lib_nl_nl_socket_modify_cb" >&6; } if test "x$ac_cv_lib_nl_nl_socket_modify_cb" = xyes; then : USE_NL="LIBIPVS_USE_NL" + USE_NL3="_HAVE_LIBNL1_" CFLAGS="$CFLAGS -DFALLBACK_LIBNL1" LIBS="$LIBS $(pkg-config --libs libnl-1)" @@ -4507,6 +4554,7 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } if ${ac_cv_c_const+:} false; then : @@ -6023,3 +6071,10 @@ if test "${DFLAGS}" = "-D_DEBUG_"; then else echo "Use Debug flags : No" fi +if test "${USE_NL3}" = "_HAVE_LIBNL3_"; then + echo "libnl version : 3" +elif test "${USE_NL3}" = "_HAVE_LIBNL1_"; then + echo "libnl version : 1" +else + echo "libnl version : None" +fi diff --git a/configure.in b/configure.in index 38a8acd..68bc336 100644 --- a/configure.in +++ b/configure.in @@ -58,6 +58,7 @@ AC_CHECK_LIB(crypt, crypt,,AC_MSG_ERROR([crypt() function is required])) AC_CHECK_LIB(crypto, MD5_Init,,AC_MSG_ERROR([OpenSSL libraries are required])) AC_CHECK_LIB(ssl, SSL_CTX_new,,AC_MSG_ERROR([OpenSSL libraries are required])) +USE_NL3="_WITHOUT_LIBNL_" AC_CHECK_LIB(nl-3, nl_socket_alloc, [ AC_CHECK_LIB(nl-genl-3, genl_connect, @@ -69,11 +70,18 @@ AC_CHECK_LIB(nl-3, nl_socket_alloc, [ AC_MSG_ERROR([libnl-3 is installed but not libnl-gen-3. Please, install libnl-gen-3.]) ]) + AC_CHECK_LIB(nl-route-3, nl_rtgen_request, + [ + USE_NL3="_HAVE_LIBNL3_" + CFLAGS="$CFLAGS $(pkg-config --cflags libnl-route-3.0)" + LIBS="$LIBS $(pkg-config --libs libnl-route-3.0)" + ]) ], [ AC_CHECK_LIB(nl, nl_socket_modify_cb, [ USE_NL="LIBIPVS_USE_NL" + USE_NL3="_HAVE_LIBNL1_" CFLAGS="$CFLAGS -DFALLBACK_LIBNL1" LIBS="$LIBS $(pkg-config --libs libnl-1)" ], @@ -101,7 +109,7 @@ AC_TRY_RUN([ #elif defined(LINUX_VERSION_CODE) fprintf (fp, "%d.%d.%d\n", LINUX_VERSION_CODE >> 16, (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF); #else - fprintf (fp, "0.0.0\n"); /* Let's fail gently */ + fprintf (fp, "0.0.0\n"); /* Let''s fail gently */ #endif fclose (fp); return 0; @@ -301,6 +309,7 @@ AC_SUBST(VERSION_DATE) AC_SUBST(IPVS_SUPPORT) AC_SUBST(USE_NL) AC_SUBST(VRRP_SUPPORT) +AC_SUBST(USE_NL3) dnl ----[ Checks for typedefs, structures, and compiler characteristics ]---- AC_C_CONST @@ -392,4 +401,11 @@ if test "${DFLAGS}" = "-D_DEBUG_"; then else echo "Use Debug flags : No" fi +if test "${USE_NL3}" = "_HAVE_LIBNL3_"; then + echo "libnl version : 3" +elif test "${USE_NL3}" = "_HAVE_LIBNL1_"; then + echo "libnl version : 1" +else + echo "libnl version : None" +fi dnl ----[ end configure ]--- diff --git a/keepalived/include/vrrp_if.h b/keepalived/include/vrrp_if.h index a8bfc1c..ec6064f 100644 --- a/keepalived/include/vrrp_if.h +++ b/keepalived/include/vrrp_if.h @@ -88,6 +88,7 @@ typedef struct _interface { int linkbeat; /* LinkBeat from MII BMSR req */ int vmac; /* Set if interface is a VMAC interface */ unsigned int base_ifindex; /* Base interface index (if interface is a VMAC interface) */ + int reset_arp_ignore; /* Set if we have set arp_ignore in interface */ } interface_t; /* Tracked interface structure definition */ diff --git a/keepalived/include/vrrp_netlink3.h b/keepalived/include/vrrp_netlink3.h new file mode 100644 index 0000000..3b5ae5e --- /dev/null +++ b/keepalived/include/vrrp_netlink3.h @@ -0,0 +1,30 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: vrrp_netlink3.c include file. + * + * Author: Alexandre Cassen, <[email protected]> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Copyright (C) 2001-2014 Alexandre Cassen, <[email protected]> + */ + +#ifndef _VRRP_NETLINK3_H +#define _VRRP_NETLINK3_H 1 + +/* prototypes */ +extern int netlink3_set_interface_parameters(int, int, int *); +extern int netlink3_reset_interface_parameters(int); + +#endif diff --git a/keepalived/vrrp/Makefile.in b/keepalived/vrrp/Makefile.in index 8f4a59b..915b51f 100644 --- a/keepalived/vrrp/Makefile.in +++ b/keepalived/vrrp/Makefile.in @@ -19,6 +19,9 @@ OBJS = vrrp_daemon.o vrrp_print.o vrrp_data.o vrrp_parser.o \ ifeq ($(SNMP_FLAG),_WITH_SNMP_) OBJS += vrrp_snmp.o endif +ifeq ($(NL3_FLAG),_HAVE_LIBNL3_) + OBJS += vrrp_netlink3.o +endif HEADERS = $(OBJS:.o=.h) .c.o: @@ -83,8 +86,9 @@ vrrp_ndisc.o: vrrp_ndisc.c ../include/vrrp_ndisc.h ../include/vrrp_ipaddress.h \ ../../lib/utils.h ../../lib/memory.h vrrp_vmac.o: vrrp_vmac.c ../include/vrrp_vmac.h ../include/vrrp_netlink.h \ ../include/vrrp_data.h ../../lib/logger.h ../../lib/memory.h ../../lib/utils.h \ - ../../lib/bitops.h + ../../lib/bitops.h ../include/vrrp_netlink3.h vrrp_snmp.o: vrrp_snmp.c ../include/vrrp_snmp.h ../include/vrrp_track.h \ ../include/vrrp_data.h ../include/vrrp_ipaddress.h ../include/vrrp_iproute.h \ ../include/vrrp_iprule.h ../include/vrrp.h ../../lib/vector.h ../../lib/list.h ../include/snmp.h \ ../include/global_data.h ../../lib/logger.h +vrrp_netlink3.o: vrrp_netlink3.c ../include/vrrp_netlink3.h ../../lib/logger.h diff --git a/keepalived/vrrp/vrrp_netlink.c b/keepalived/vrrp/vrrp_netlink.c index 7ac851b..bb52143 100644 --- a/keepalived/vrrp/vrrp_netlink.c +++ b/keepalived/vrrp/vrrp_netlink.c @@ -477,6 +477,7 @@ netlink_if_link_filter(struct sockaddr_nl *snl, struct nlmsghdr *h) ifp->ifindex = ifi->ifi_index; ifp->mtu = *(int *) RTA_DATA(tb[IFLA_MTU]); ifp->hw_type = ifi->ifi_type; + ifp->reset_arp_ignore = 0; if (!ifp->vmac) { if_vmac_reflect_flags(ifi->ifi_index, ifi->ifi_flags); diff --git a/keepalived/vrrp/vrrp_netlink3.c b/keepalived/vrrp/vrrp_netlink3.c new file mode 100644 index 0000000..2b75758 --- /dev/null +++ b/keepalived/vrrp/vrrp_netlink3.c @@ -0,0 +1,156 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: libnl3 interface + * + * Author: Alexandre Cassen, <[email protected]> + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Copyright (C) 2001-2014 Alexandre Cassen, <[email protected]> + */ + +#include <netlink/netlink.h> +#include <netlink/route/link.h> +#include <netlink/route/link/inet.h> +#include <linux/ip.h> +#include <syslog.h> + +#include "logger.h" + +int +netlink3_set_interface_parameters(int ifindex, int base_ifindex, int *reset_arp_ignore) +{ + struct nl_sock *sk; + struct nl_cache *cache; + struct rtnl_link *link = NULL; + struct rtnl_link *new_state = NULL; + uint32_t base_arp_ignore; + int res = 0; + + if (!(sk = nl_socket_alloc())) { + log_message(LOG_INFO, "Unable to open netlink socket"); + return -1; + } + + if (nl_connect(sk, NETLINK_ROUTE) < 0) + goto err; + if (rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache)) + goto err; + if (!(link = rtnl_link_get(cache, ifindex))) + goto err; + + // Allocate a new link + if (!(new_state = rtnl_link_alloc())) + goto err; + + if (rtnl_link_inet_set_conf(new_state, IPV4_DEVCONF_ARP_IGNORE, 2) || + rtnl_link_inet_set_conf(new_state, IPV4_DEVCONF_RP_FILTER, 0) || + rtnl_link_change (sk, link, new_state, 0)) + goto err; + + rtnl_link_put(new_state); + new_state = NULL; + + rtnl_link_put(link); + link = NULL; + + /* Set arp_ignore on base interface if needed */ + if (*reset_arp_ignore) + (*reset_arp_ignore)++; + else { + if (!(link = rtnl_link_get(cache, base_ifindex))) + goto err; + if (rtnl_link_inet_get_conf(link, IPV4_DEVCONF_ARP_IGNORE, &base_arp_ignore) < 0) + goto err; + + if (base_arp_ignore == 0) { + /* The underlying interface mustn't reply for our address(es) */ + if (!(new_state = rtnl_link_alloc())) + goto err; + + if (rtnl_link_inet_set_conf(new_state, IPV4_DEVCONF_ARP_IGNORE, 1) || + rtnl_link_change(sk, link, new_state, 0)) + goto err; + + rtnl_link_put(new_state); + new_state = NULL; + + rtnl_link_put(link); + link = NULL; + + *reset_arp_ignore = 1; + } + } + + goto exit; +err: + res = -1; + + if (link) + rtnl_link_put(link); + if (new_state) + rtnl_link_put(new_state); + +exit: + nl_close ( sk ) ; + + return res; +} + +int +netlink3_reset_interface_parameters(int ifindex) +{ + struct nl_sock *sk; + struct nl_cache *cache; + struct rtnl_link *link = NULL; + struct rtnl_link *new_state = NULL; + int res = 0; + + if (!(sk = nl_socket_alloc())) { + log_message(LOG_INFO, "Unable to open netlink socket"); + return -1; + } + + if (nl_connect(sk, NETLINK_ROUTE) < 0) + goto err; + if (rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache)) + goto err; + if (!(link = rtnl_link_get(cache, ifindex))) + goto err; + if (!(new_state = rtnl_link_alloc())) + goto err; + if (rtnl_link_inet_set_conf(new_state, IPV4_DEVCONF_ARP_IGNORE, 0) || + rtnl_link_change(sk, link, new_state, 0)) + goto err; + + rtnl_link_put(link); + link = NULL; + + rtnl_link_put(new_state); + new_state = NULL; + + goto exit; +err: + res = -1; + + if (link) + rtnl_link_put(link); + if (new_state) + rtnl_link_put(new_state); + +exit: + nl_close(sk); + + return res; +} diff --git a/keepalived/vrrp/vrrp_vmac.c b/keepalived/vrrp/vrrp_vmac.c index b4634f9..e80135e 100644 --- a/keepalived/vrrp/vrrp_vmac.c +++ b/keepalived/vrrp/vrrp_vmac.c @@ -20,6 +20,9 @@ * Copyright (C) 2001-2012 Alexandre Cassen, <[email protected]> */ +/* global include */ +#include <limits.h> + /* local include */ #include "vrrp_vmac.h" #include "vrrp_netlink.h" @@ -29,11 +32,126 @@ #include "utils.h" #include "parser.h" #include "bitops.h" +#ifdef _HAVE_LIBNL3_ +#include "vrrp_netlink3.h" +#endif #ifdef _HAVE_VRRP_VMAC_ /* private matter */ static const char *ll_kind = "macvlan"; +/* The following parameters need to be set on the vmac interface and its parent: + * vmac interface: + * arp_ignore=2 // We mustn't reply to ARP requests on this interface for IP address on parent interface + * // and we only need to reply to addresses on the same subnet. + * rp_filter=0 // Allows us to receive on VMAC interface when it has no IP address. + * parent interface: + * arp_ignore=1 // We mustn't reply to ARP requests on this interface for vrrp IP address + */ +#ifdef _HAVE_LIBNL3_ +static void +set_interface_parameters(const interface_t *ifp, interface_t *base_ifp) +{ + if (netlink3_set_interface_parameters(ifp->ifindex, base_ifp->ifindex, &base_ifp->reset_arp_ignore)) + log_message(LOG_INFO, "Unable to set parameters for %s", ifp->ifname); +} + +static void +reset_interface_parameters(interface_t *base_ifp) +{ + if (netlink3_reset_interface_parameters(base_ifp->ifindex)) + log_message(LOG_INFO, "Unable to reset parameters for %s", base_ifp->ifname); +} + +#else +/* Sysctl get and set functions */ +static void +make_sysctl_filename(char *dest, const char* prefix, const char* iface, const char* parameter) +{ + strcpy(dest, "/proc/sys/"); + strcat(dest, prefix); + strcat(dest, "/"); + strcat(dest, iface); + strcat(dest, "/"); + strcat(dest, parameter); +} + +static int +get_sysctl(const char* prefix, const char* iface, const char* parameter) +{ + char *filename; + char buf[1]; + int fd; + int len; + + /* Make the filename */ + filename = MALLOC(PATH_MAX); + make_sysctl_filename(filename, prefix, iface, parameter); + + fd = open(filename, O_RDONLY); + FREE(filename); + if (fd<0) + return -1; + + len = read(fd, &buf, 1); + close(fd); + + /* We only read integers 0-9 */ + if (len <= 0) + return -1; + + /* Return the value of the string read */ + return buf[0] - '0'; +} + +static int +set_sysctl(const char* prefix, const char* iface, const char* parameter, int value) +{ + char* filename; + char buf[1]; + int fd; + int len; + + /* Make the filename */ + filename = MALLOC(PATH_MAX); + make_sysctl_filename(filename, prefix, iface, parameter); + + fd = open(filename, O_WRONLY); + FREE(filename); + if (fd <0) + return -1; + + /* We only write integers 0-9 */ + buf[0] = '0' + value; + len = write(fd, &buf, 1); + close(fd); + + if (len != 1) + return -1; + + /* Success */ + return 0; +} + +void +set_interface_parameters(interface_t *ifp, interface_t *base_ifp) +{ + set_sysctl("net/ipv4/conf", ifp->ifname, "arp_ignore", 2); + if (base_ifp->reset_arp_ignore) + base_ifp->reset_arp_ignore++; + else if (get_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore") == 0) { + set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore", 1); + base_ifp->reset_arp_ignore = 1; + } + set_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter", 0); +} + +void reset_interface_parameters(interface_t *base_ifp) +{ + set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore", 0); +} +#endif + static int netlink_link_up(vrrp_t *vrrp) { @@ -69,6 +187,7 @@ netlink_link_add_vmac(vrrp_t *vrrp) struct rtattr *data; unsigned int base_ifindex; interface_t *ifp; + interface_t *base_ifp; char ifname[IFNAMSIZ]; u_char ll_addr[ETH_ALEN] = {0x00, 0x00, 0x5e, 0x00, 0x01, vrrp->vrid}; struct { @@ -175,6 +294,7 @@ netlink_link_add_vmac(vrrp_t *vrrp) ifp = if_get_by_ifname(ifname); if (!ifp) return -1; + base_ifp = vrrp->ifp; base_ifindex = vrrp->ifp->ifindex; ifp->flags = vrrp->ifp->flags; /* Copy base interface flags */ vrrp->ifp = ifp; @@ -183,6 +303,11 @@ netlink_link_add_vmac(vrrp_t *vrrp) vrrp->vmac_ifindex = IF_INDEX(vrrp->ifp); /* For use on delete */ __set_bit(VRRP_VMAC_UP_BIT, &vrrp->vmac_flags); netlink_link_up(vrrp); + + if (vrrp->family == AF_INET) { + /* Set the necessary kernel parameters to make macvlans work for us */ + set_interface_parameters(ifp, base_ifp); + } #endif return 1; } @@ -193,6 +318,7 @@ netlink_link_del_vmac(vrrp_t *vrrp) int status = 1; #ifdef _HAVE_VRRP_VMAC_ + interface_t *base_ifp ; struct { struct nlmsghdr n; struct ifinfomsg ifi; @@ -202,6 +328,12 @@ netlink_link_del_vmac(vrrp_t *vrrp) if (!vrrp->ifp) return -1; + /* Reset arp_ignore on the base interface if necessary */ + base_ifp = if_get_by_ifindex(vrrp->ifp->base_ifindex); + if (base_ifp->reset_arp_ignore && --base_ifp->reset_arp_ignore == 0) { + reset_interface_parameters(base_ifp); + } + memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); -- 1.7.7.6 ------------------------------------------------------------------------------