mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-12-18 05:53:36 +01:00
3711515467
Allow carp(4) to use the VRRPv3 protocol (RFC 5798). We can distinguish carp and VRRP based on the protocol version number (carp is 2, VRRPv3 is 3), and support both from the carp(4) code. Reviewed by: glebius Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D44774
303 lines
8.1 KiB
C
303 lines
8.1 KiB
C
/* from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */
|
|
|
|
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
|
|
* Copyright (c) 2003 Ryan McBride. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip_carp.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
|
|
#include <libifconfig.h>
|
|
|
|
#include "ifconfig.h"
|
|
|
|
static const char *carp_states[] = { CARP_STATES };
|
|
|
|
static void setcarp_callback(if_ctx *, void *);
|
|
|
|
static int carpr_vhid = -1;
|
|
static int carpr_advskew = -1;
|
|
static int carpr_advbase = -1;
|
|
static int carpr_state = -1;
|
|
static struct in_addr carp_addr;
|
|
static struct in6_addr carp_addr6;
|
|
static unsigned char const *carpr_key;
|
|
static carp_version_t carpr_version;
|
|
static uint8_t carpr_vrrp_prio;
|
|
static uint16_t carpr_vrrp_adv_inter;
|
|
|
|
static void
|
|
carp_status(if_ctx *ctx)
|
|
{
|
|
struct ifconfig_carp carpr[CARP_MAXVHID];
|
|
char addr_buf[NI_MAXHOST];
|
|
|
|
if (ifconfig_carp_get_info(lifh, ctx->ifname, carpr, CARP_MAXVHID) == -1)
|
|
return;
|
|
|
|
for (size_t i = 0; i < carpr[0].carpr_count; i++) {
|
|
switch (carpr[i].carpr_version) {
|
|
case CARP_VERSION_CARP:
|
|
printf("\tcarp: %s vhid %d advbase %d advskew %d",
|
|
carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
|
|
carpr[i].carpr_advbase, carpr[i].carpr_advskew);
|
|
if (ctx->args->printkeys && carpr[i].carpr_key[0] != '\0')
|
|
printf(" key \"%s\"\n", carpr[i].carpr_key);
|
|
else
|
|
printf("\n");
|
|
|
|
inet_ntop(AF_INET6, &carpr[i].carpr_addr6, addr_buf,
|
|
sizeof(addr_buf));
|
|
|
|
printf("\t peer %s peer6 %s\n",
|
|
inet_ntoa(carpr[i].carpr_addr), addr_buf);
|
|
break;
|
|
case CARP_VERSION_VRRPv3:
|
|
printf("\tvrrp: %s vrid %d prio %d interval %d\n",
|
|
carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
|
|
carpr[i].carpr_vrrp_prio, carpr[i].carpr_vrrp_adv_inter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setcarp_vhid(if_ctx *ctx, const char *val, int dummy __unused)
|
|
{
|
|
const struct afswtch *afp = ctx->afp;
|
|
|
|
carpr_vhid = atoi(val);
|
|
|
|
if (carpr_vhid <= 0 || carpr_vhid > CARP_MAXVHID)
|
|
errx(1, "vhid must be greater than 0 and less than %u",
|
|
CARP_MAXVHID);
|
|
|
|
if (afp->af_setvhid == NULL)
|
|
errx(1, "%s doesn't support carp(4)", afp->af_name);
|
|
afp->af_setvhid(carpr_vhid);
|
|
callback_register(setcarp_callback, NULL);
|
|
}
|
|
|
|
static void
|
|
setcarp_callback(if_ctx *ctx, void *arg __unused)
|
|
{
|
|
struct ifconfig_carp carpr = { };
|
|
|
|
if (ifconfig_carp_get_vhid(lifh, ctx->ifname, &carpr, carpr_vhid) == -1) {
|
|
if (ifconfig_err_errno(lifh) != ENOENT)
|
|
return;
|
|
}
|
|
|
|
carpr.carpr_vhid = carpr_vhid;
|
|
if (carpr_key != NULL)
|
|
/* XXX Should hash the password into the key here? */
|
|
strlcpy(carpr.carpr_key, carpr_key, CARP_KEY_LEN);
|
|
if (carpr_advskew > -1)
|
|
carpr.carpr_advskew = carpr_advskew;
|
|
if (carpr_advbase > -1)
|
|
carpr.carpr_advbase = carpr_advbase;
|
|
if (carpr_state > -1)
|
|
carpr.carpr_state = carpr_state;
|
|
if (carp_addr.s_addr != INADDR_ANY)
|
|
carpr.carpr_addr = carp_addr;
|
|
if (! IN6_IS_ADDR_UNSPECIFIED(&carp_addr6))
|
|
memcpy(&carpr.carpr_addr6, &carp_addr6,
|
|
sizeof(carp_addr6));
|
|
if (carpr_version != 0)
|
|
carpr.carpr_version = carpr_version;
|
|
if (carpr_vrrp_prio != 0)
|
|
carpr.carpr_vrrp_prio = carpr_vrrp_prio;
|
|
if (carpr_vrrp_adv_inter != 0)
|
|
carpr.carpr_vrrp_adv_inter = carpr_vrrp_adv_inter;
|
|
|
|
if (ifconfig_carp_set_info(lifh, ctx->ifname, &carpr))
|
|
err(1, "SIOCSVH");
|
|
}
|
|
|
|
static void
|
|
setcarp_passwd(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
|
|
if (carpr_vhid == -1)
|
|
errx(1, "passwd requires vhid");
|
|
|
|
carpr_key = val;
|
|
}
|
|
|
|
static void
|
|
setcarp_advskew(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
|
|
if (carpr_vhid == -1)
|
|
errx(1, "advskew requires vhid");
|
|
|
|
carpr_advskew = atoi(val);
|
|
}
|
|
|
|
static void
|
|
setcarp_advbase(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
|
|
if (carpr_vhid == -1)
|
|
errx(1, "advbase requires vhid");
|
|
|
|
carpr_advbase = atoi(val);
|
|
}
|
|
|
|
static void
|
|
setcarp_state(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
int i;
|
|
|
|
if (carpr_vhid == -1)
|
|
errx(1, "state requires vhid");
|
|
|
|
for (i = 0; i <= CARP_MAXSTATE; i++)
|
|
if (strcasecmp(carp_states[i], val) == 0) {
|
|
carpr_state = i;
|
|
return;
|
|
}
|
|
|
|
errx(1, "unknown state");
|
|
}
|
|
|
|
static void
|
|
setcarp_peer(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
carp_addr.s_addr = inet_addr(val);
|
|
}
|
|
|
|
static void
|
|
setcarp_mcast(if_ctx *ctx __unused, const char *val __unused, int dummy __unused)
|
|
{
|
|
carp_addr.s_addr = htonl(INADDR_CARP_GROUP);
|
|
}
|
|
|
|
static void
|
|
setcarp_peer6(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
struct addrinfo hints, *res;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET6;
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
if (getaddrinfo(val, NULL, &hints, &res) != 0)
|
|
errx(1, "Invalid IPv6 address %s", val);
|
|
|
|
memcpy(&carp_addr6, &(satosin6(res->ai_addr))->sin6_addr, sizeof(carp_addr6));
|
|
freeaddrinfo(res);
|
|
}
|
|
|
|
static void
|
|
setcarp_mcast6(if_ctx *ctx __unused, const char *val __unused, int dummy __unused)
|
|
{
|
|
bzero(&carp_addr6, sizeof(carp_addr6));
|
|
carp_addr6.s6_addr[0] = 0xff;
|
|
carp_addr6.s6_addr[1] = 0x02;
|
|
carp_addr6.s6_addr[15] = 0x12;
|
|
}
|
|
|
|
static void
|
|
setcarp_version(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
carpr_version = atoi(val);
|
|
|
|
if (carpr_version != CARP_VERSION_CARP && carpr_version != CARP_VERSION_VRRPv3)
|
|
errx(1, "version must be %d or %d", CARP_VERSION_CARP,
|
|
CARP_VERSION_VRRPv3);
|
|
}
|
|
|
|
static void
|
|
setvrrp_prio(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
carpr_vrrp_prio = atoi(val);
|
|
}
|
|
|
|
static void
|
|
setvrrp_interval(if_ctx *ctx __unused, const char *val, int dummy __unused)
|
|
{
|
|
carpr_vrrp_adv_inter = atoi(val);
|
|
|
|
if (carpr_vrrp_adv_inter == 0 || carpr_vrrp_adv_inter > VRRP_MAX_INTERVAL)
|
|
errx(1, "vrrpinterval must be greater than 0 and less than %d", VRRP_MAX_INTERVAL);
|
|
}
|
|
|
|
static struct cmd carp_cmds[] = {
|
|
DEF_CMD_ARG("advbase", setcarp_advbase),
|
|
DEF_CMD_ARG("advskew", setcarp_advskew),
|
|
DEF_CMD_ARG("pass", setcarp_passwd),
|
|
DEF_CMD_ARG("vhid", setcarp_vhid),
|
|
DEF_CMD_ARG("state", setcarp_state),
|
|
DEF_CMD_ARG("peer", setcarp_peer),
|
|
DEF_CMD("mcast", 0, setcarp_mcast),
|
|
DEF_CMD_ARG("peer6", setcarp_peer6),
|
|
DEF_CMD("mcast6", 0, setcarp_mcast6),
|
|
DEF_CMD_ARG("carpver", setcarp_version),
|
|
DEF_CMD_ARG("vrrpprio", setvrrp_prio),
|
|
DEF_CMD_ARG("vrrpinterval", setvrrp_interval),
|
|
};
|
|
static struct afswtch af_carp = {
|
|
.af_name = "af_carp",
|
|
.af_af = AF_UNSPEC,
|
|
.af_other_status = carp_status,
|
|
};
|
|
|
|
static __constructor void
|
|
carp_ctor(void)
|
|
{
|
|
/* Default to multicast. */
|
|
setcarp_mcast(NULL, NULL, 0);
|
|
setcarp_mcast6(NULL, NULL, 0);
|
|
|
|
for (size_t i = 0; i < nitems(carp_cmds); i++)
|
|
cmd_register(&carp_cmds[i]);
|
|
af_register(&af_carp);
|
|
}
|