mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
carp: add netlink interface
Allow carp configuration information to be supplied and retrieved via netlink. Reviewed by: melifaro Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D39048
This commit is contained in:
parent
62b572694b
commit
40e0435964
|
@ -33,6 +33,7 @@
|
|||
#include <net/if.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_carp.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
|
||||
#define ND6_IFF_DEFAULTIF 0x8000
|
||||
|
@ -41,7 +42,8 @@ typedef enum {
|
|||
OK = 0,
|
||||
OTHER,
|
||||
IOCTL,
|
||||
SOCKET
|
||||
SOCKET,
|
||||
NETLINK
|
||||
} ifconfig_errtype;
|
||||
|
||||
/*
|
||||
|
@ -51,7 +53,6 @@ typedef enum {
|
|||
struct ifconfig_handle;
|
||||
typedef struct ifconfig_handle ifconfig_handle_t;
|
||||
|
||||
struct carpreq;
|
||||
struct ifaddrs;
|
||||
struct ifbropreq;
|
||||
struct ifbreq;
|
||||
|
@ -279,8 +280,21 @@ ifmedia_t *ifconfig_media_lookup_options(ifmedia_t media, const char **opts,
|
|||
int ifconfig_media_get_downreason(ifconfig_handle_t *h, const char *name,
|
||||
struct ifdownreason *ifdr);
|
||||
|
||||
struct ifconfig_carp {
|
||||
size_t carpr_count;
|
||||
uint32_t carpr_vhid;
|
||||
uint32_t carpr_state;
|
||||
int32_t carpr_advbase;
|
||||
int32_t carpr_advskew;
|
||||
uint8_t carpr_key[CARP_KEY_LEN];
|
||||
};
|
||||
|
||||
int ifconfig_carp_get_vhid(ifconfig_handle_t *h, const char *name,
|
||||
struct ifconfig_carp *carpr, uint32_t vhid);
|
||||
int ifconfig_carp_get_info(ifconfig_handle_t *h, const char *name,
|
||||
struct carpreq *carpr, int ncarpr);
|
||||
struct ifconfig_carp *carpr, size_t ncarp);
|
||||
int ifconfig_carp_set_info(ifconfig_handle_t *h, const char *name,
|
||||
const struct ifconfig_carp *carpr);
|
||||
|
||||
/** Retrieve additional information about an inet address
|
||||
* @param h An open ifconfig state object
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
|
||||
#include <net/if.h>
|
||||
#include <netinet/ip_carp.h>
|
||||
#include <netinet/ip_carp_nl.h>
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_generic.h>
|
||||
#include <netlink/netlink_snl.h>
|
||||
#include <netlink/netlink_snl_generic.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
@ -40,21 +46,174 @@
|
|||
#include "libifconfig.h"
|
||||
#include "libifconfig_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
ifconfig_carp_get_info(ifconfig_handle_t *h, const char *name,
|
||||
struct carpreq *carpr, int ncarpr)
|
||||
#define _OUT(_field) offsetof(struct ifconfig_carp, _field)
|
||||
static struct snl_attr_parser ap_carp_get[] = {
|
||||
{ .type = CARP_NL_VHID, .off = _OUT(carpr_vhid), .cb = snl_attr_get_uint32 },
|
||||
{ .type = CARP_NL_STATE, .off = _OUT(carpr_state), .cb = snl_attr_get_uint32 },
|
||||
{ .type = CARP_NL_ADVBASE, .off = _OUT(carpr_advbase), .cb = snl_attr_get_int32 },
|
||||
{ .type = CARP_NL_ADVSKEW, .off = _OUT(carpr_advskew), .cb = snl_attr_get_int32 },
|
||||
{ .type = CARP_NL_KEY, .off = _OUT(carpr_key), .cb = snl_attr_get_string },
|
||||
};
|
||||
#undef _OUT
|
||||
|
||||
SNL_DECLARE_GENL_PARSER(carp_get_parser, ap_carp_get);
|
||||
|
||||
static int
|
||||
_ifconfig_carp_get(ifconfig_handle_t *h, const char *name,
|
||||
struct ifconfig_carp *carp, size_t ncarp, uint32_t vhid)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
struct snl_state ss = {};
|
||||
struct snl_errmsg_data e = {};
|
||||
struct snl_writer nw;
|
||||
struct nlmsghdr *hdr;
|
||||
size_t i = 0;
|
||||
uint32_t seq_id;
|
||||
unsigned int ifindex;
|
||||
int family_id;
|
||||
|
||||
bzero(carpr, sizeof(struct carpreq) * ncarpr);
|
||||
carpr[0].carpr_count = ncarpr;
|
||||
strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
|
||||
ifr.ifr_data = (caddr_t)carpr;
|
||||
ifconfig_error_clear(h);
|
||||
|
||||
if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGVH, &ifr) != 0) {
|
||||
ifindex = if_nametoindex(name);
|
||||
if (ifindex == 0) {
|
||||
ifconfig_error(h, NETLINK, ENOENT);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
if (! snl_init(&ss, NETLINK_GENERIC)) {
|
||||
ifconfig_error(h, NETLINK, ENOTSUP);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
|
||||
family_id = snl_get_genl_family(&ss, CARP_NL_FAMILY_NAME);
|
||||
if (family_id == 0) {
|
||||
ifconfig_error(h, NETLINK, EPROTONOSUPPORT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hdr = snl_create_genl_msg_request(&nw, family_id, CARP_NL_CMD_GET);
|
||||
hdr->nlmsg_flags |= NLM_F_DUMP;
|
||||
|
||||
snl_add_msg_attr_u32(&nw, CARP_NL_IFINDEX, ifindex);
|
||||
|
||||
if (vhid != 0)
|
||||
snl_add_msg_attr_u32(&nw, CARP_NL_VHID, vhid);
|
||||
|
||||
hdr = snl_finalize_msg(&nw);
|
||||
if (hdr == NULL) {
|
||||
ifconfig_error(h, NETLINK, ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
seq_id = hdr->nlmsg_seq;
|
||||
if (! snl_send_message(&ss, hdr)) {
|
||||
ifconfig_error(h, NETLINK, EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
|
||||
if (e.error != 0) {
|
||||
ifconfig_error(h, NETLINK, e.error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ncarp) {
|
||||
ifconfig_error(h, NETLINK, E2BIG);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&carp[i], 0, sizeof(carp[0]));
|
||||
if (! snl_parse_nlmsg(&ss, hdr, &carp_get_parser, &carp[i]))
|
||||
continue;
|
||||
|
||||
i++;
|
||||
carp[0].carpr_count = i;
|
||||
|
||||
if (i > ncarp) {
|
||||
ifconfig_error(h, NETLINK, E2BIG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
snl_free(&ss);
|
||||
|
||||
return (h->error.errcode ? -1 : 0);
|
||||
}
|
||||
|
||||
int
|
||||
ifconfig_carp_set_info(ifconfig_handle_t *h, const char *name,
|
||||
const struct ifconfig_carp *carpr)
|
||||
{
|
||||
struct snl_state ss = {};
|
||||
struct snl_writer nw;
|
||||
struct nlmsghdr *hdr;
|
||||
unsigned int ifindex;
|
||||
int family_id;
|
||||
uint32_t seq_id;
|
||||
|
||||
ifconfig_error_clear(h);
|
||||
|
||||
ifindex = if_nametoindex(name);
|
||||
if (ifindex == 0) {
|
||||
ifconfig_error(h, NETLINK, ENOENT);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (! snl_init(&ss, NETLINK_GENERIC)) {
|
||||
ifconfig_error(h, NETLINK, ENOTSUP);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
snl_init_writer(&ss, &nw);
|
||||
|
||||
family_id = snl_get_genl_family(&ss, CARP_NL_FAMILY_NAME);
|
||||
if (family_id == 0) {
|
||||
ifconfig_error(h, NETLINK, EPROTONOSUPPORT);
|
||||
return (-1);
|
||||
}
|
||||
hdr = snl_create_genl_msg_request(&nw, family_id, CARP_NL_CMD_SET);
|
||||
|
||||
snl_add_msg_attr_u32(&nw, CARP_NL_VHID, carpr->carpr_vhid);
|
||||
snl_add_msg_attr_u32(&nw, CARP_NL_STATE, carpr->carpr_state);
|
||||
snl_add_msg_attr_s32(&nw, CARP_NL_ADVBASE, carpr->carpr_advbase);
|
||||
snl_add_msg_attr_s32(&nw, CARP_NL_ADVSKEW, carpr->carpr_advskew);
|
||||
snl_add_msg_attr_u32(&nw, CARP_NL_IFINDEX, ifindex);
|
||||
|
||||
hdr = snl_finalize_msg(&nw);
|
||||
if (hdr == NULL) {
|
||||
ifconfig_error(h, NETLINK, ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
seq_id = hdr->nlmsg_seq;
|
||||
if (! snl_send_message(&ss, hdr)) {
|
||||
ifconfig_error(h, NETLINK, EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct snl_errmsg_data e = { };
|
||||
if (! snl_read_reply_code(&ss, seq_id, &e))
|
||||
ifconfig_error(h, NETLINK, e.error);
|
||||
|
||||
out:
|
||||
snl_free(&ss);
|
||||
|
||||
return (h->error.errcode ? -1 : 0);
|
||||
}
|
||||
|
||||
int
|
||||
ifconfig_carp_get_vhid(ifconfig_handle_t *h, const char *name,
|
||||
struct ifconfig_carp *carp, uint32_t vhid)
|
||||
{
|
||||
return (_ifconfig_carp_get(h, name, carp, 1, vhid));
|
||||
}
|
||||
|
||||
int
|
||||
ifconfig_carp_get_info(ifconfig_handle_t *h, const char *name,
|
||||
struct ifconfig_carp *carp, size_t ncarp)
|
||||
{
|
||||
return (_ifconfig_carp_get(h, name, carp, ncarp, 0));
|
||||
}
|
||||
|
|
|
@ -102,3 +102,17 @@ ifconfig_socket(ifconfig_handle_t *h, const int addressfamily, int *s)
|
|||
*s = h->sockets[addressfamily];
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
ifconfig_error_clear(ifconfig_handle_t *h)
|
||||
{
|
||||
h->error.errtype = OK;
|
||||
h->error.errcode = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ifconfig_error(ifconfig_handle_t *h, ifconfig_errtype type, int error)
|
||||
{
|
||||
h->error.errtype = type;
|
||||
h->error.errcode = error;
|
||||
}
|
||||
|
|
|
@ -83,3 +83,6 @@ int ifconfig_socket(ifconfig_handle_t *h, const int addressfamily, int *s);
|
|||
/** Function to wrap ioctl() and automatically populate ifconfig_errstate when appropriate.*/
|
||||
int ifconfig_ioctlwrap(ifconfig_handle_t *h, const int addressfamily,
|
||||
unsigned long request, void *data);
|
||||
|
||||
void ifconfig_error_clear(ifconfig_handle_t *h);
|
||||
void ifconfig_error(ifconfig_handle_t *h, ifconfig_errtype type, int error);
|
||||
|
|
|
@ -72,7 +72,7 @@ static unsigned char const *carpr_key;
|
|||
static void
|
||||
carp_status(int s)
|
||||
{
|
||||
struct carpreq carpr[CARP_MAXVHID];
|
||||
struct ifconfig_carp carpr[CARP_MAXVHID];
|
||||
|
||||
if (ifconfig_carp_get_info(lifh, name, carpr, CARP_MAXVHID) == -1)
|
||||
return;
|
||||
|
@ -129,16 +129,14 @@ setcarp_vhid(const char *val, int d, int s, const struct afswtch *afp)
|
|||
static void
|
||||
setcarp_callback(int s, void *arg __unused)
|
||||
{
|
||||
struct carpreq carpr;
|
||||
struct ifconfig_carp carpr = { };
|
||||
|
||||
if (ifconfig_carp_get_vhid(lifh, name, &carpr, carpr_vhid) == -1) {
|
||||
if (ifconfig_err_errno(lifh) != ENOENT)
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(&carpr, sizeof(struct carpreq));
|
||||
carpr.carpr_vhid = carpr_vhid;
|
||||
carpr.carpr_count = 1;
|
||||
ifr.ifr_data = (caddr_t)&carpr;
|
||||
|
||||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1 && errno != ENOENT)
|
||||
err(1, "SIOCGVH");
|
||||
|
||||
if (carpr_key != NULL)
|
||||
/* XXX Should hash the password into the key here? */
|
||||
strlcpy(carpr.carpr_key, carpr_key, CARP_KEY_LEN);
|
||||
|
@ -149,7 +147,7 @@ setcarp_callback(int s, void *arg __unused)
|
|||
if (carpr_state > -1)
|
||||
carpr.carpr_state = carpr_state;
|
||||
|
||||
if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
|
||||
if (ifconfig_carp_set_info(lifh, name, &carpr))
|
||||
err(1, "SIOCSVH");
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
|
|||
#include <netinet/in.h>
|
||||
#include <netinet/in_var.h>
|
||||
#include <netinet/ip_carp.h>
|
||||
#include <netinet/ip_carp_nl.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <machine/in_cksum.h>
|
||||
#endif
|
||||
|
@ -84,6 +85,11 @@ __FBSDID("$FreeBSD$");
|
|||
#include <netinet6/nd6.h>
|
||||
#endif
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_ctl.h>
|
||||
#include <netlink/netlink_generic.h>
|
||||
#include <netlink/netlink_message_parser.h>
|
||||
|
||||
#include <crypto/sha1.h>
|
||||
|
||||
static MALLOC_DEFINE(M_CARP, "CARP", "CARP addresses");
|
||||
|
@ -332,6 +338,24 @@ static struct sx carp_sx;
|
|||
static struct task carp_sendall_task =
|
||||
TASK_INITIALIZER(0, carp_send_ad_all, NULL);
|
||||
|
||||
static int
|
||||
carp_is_supported_if(if_t ifp)
|
||||
{
|
||||
if (ifp == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
switch (ifp->if_type) {
|
||||
case IFT_ETHER:
|
||||
case IFT_L2VLAN:
|
||||
case IFT_BRIDGE:
|
||||
break;
|
||||
default:
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
carp_hmac_prepare(struct carp_softc *sc)
|
||||
{
|
||||
|
@ -1709,9 +1733,10 @@ carp_free_if(struct carp_if *cif)
|
|||
free(cif, M_CARP);
|
||||
}
|
||||
|
||||
static void
|
||||
carp_carprcp(struct carpreq *carpr, struct carp_softc *sc, int priv)
|
||||
static bool
|
||||
carp_carprcp(void *arg, struct carp_softc *sc, int priv)
|
||||
{
|
||||
struct carpreq *carpr = arg;
|
||||
|
||||
CARP_LOCK(sc);
|
||||
carpr->carpr_state = sc->sc_state;
|
||||
|
@ -1723,33 +1748,142 @@ carp_carprcp(struct carpreq *carpr, struct carp_softc *sc, int priv)
|
|||
else
|
||||
bzero(carpr->carpr_key, sizeof(carpr->carpr_key));
|
||||
CARP_UNLOCK(sc);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
carp_ioctl_set(if_t ifp, struct carpreq *carpr)
|
||||
{
|
||||
struct epoch_tracker et;
|
||||
struct carp_softc *sc = NULL;
|
||||
int error = 0;
|
||||
|
||||
|
||||
if (carpr->carpr_vhid <= 0 || carpr->carpr_vhid > CARP_MAXVHID ||
|
||||
carpr->carpr_advbase < 0 || carpr->carpr_advskew < 0) {
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (ifp->if_carp) {
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
if (sc->sc_vhid == carpr->carpr_vhid)
|
||||
break;
|
||||
}
|
||||
if (sc == NULL) {
|
||||
sc = carp_alloc(ifp);
|
||||
CARP_LOCK(sc);
|
||||
sc->sc_vhid = carpr->carpr_vhid;
|
||||
LLADDR(&sc->sc_addr)[0] = 0;
|
||||
LLADDR(&sc->sc_addr)[1] = 0;
|
||||
LLADDR(&sc->sc_addr)[2] = 0x5e;
|
||||
LLADDR(&sc->sc_addr)[3] = 0;
|
||||
LLADDR(&sc->sc_addr)[4] = 1;
|
||||
LLADDR(&sc->sc_addr)[5] = sc->sc_vhid;
|
||||
} else
|
||||
CARP_LOCK(sc);
|
||||
if (carpr->carpr_advbase > 0) {
|
||||
if (carpr->carpr_advbase > 255 ||
|
||||
carpr->carpr_advbase < CARP_DFLTINTV) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
sc->sc_advbase = carpr->carpr_advbase;
|
||||
}
|
||||
if (carpr->carpr_advskew >= 255) {
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
sc->sc_advskew = carpr->carpr_advskew;
|
||||
if (carpr->carpr_key[0] != '\0') {
|
||||
bcopy(carpr->carpr_key, sc->sc_key, sizeof(sc->sc_key));
|
||||
carp_hmac_prepare(sc);
|
||||
}
|
||||
if (sc->sc_state != INIT &&
|
||||
carpr->carpr_state != sc->sc_state) {
|
||||
switch (carpr->carpr_state) {
|
||||
case BACKUP:
|
||||
callout_stop(&sc->sc_ad_tmo);
|
||||
carp_set_state(sc, BACKUP,
|
||||
"user requested via ifconfig");
|
||||
carp_setrun(sc, 0);
|
||||
carp_delroute(sc);
|
||||
break;
|
||||
case MASTER:
|
||||
NET_EPOCH_ENTER(et);
|
||||
carp_master_down_locked(sc,
|
||||
"user requested via ifconfig");
|
||||
NET_EPOCH_EXIT(et);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
CARP_UNLOCK(sc);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
carp_ioctl_get(if_t ifp, struct ucred *cred, struct carpreq *carpr,
|
||||
bool (*outfn)(void *, struct carp_softc *, int), void *arg)
|
||||
{
|
||||
int priveleged;
|
||||
struct carp_softc *sc;
|
||||
|
||||
if (carpr->carpr_vhid < 0 || carpr->carpr_vhid > CARP_MAXVHID)
|
||||
return (EINVAL);
|
||||
if (carpr->carpr_count < 1)
|
||||
return (EMSGSIZE);
|
||||
if (ifp->if_carp == NULL)
|
||||
return (ENOENT);
|
||||
|
||||
priveleged = (priv_check_cred(cred, PRIV_NETINET_CARP) == 0);
|
||||
if (carpr->carpr_vhid != 0) {
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
if (sc->sc_vhid == carpr->carpr_vhid)
|
||||
break;
|
||||
if (sc == NULL)
|
||||
return (ENOENT);
|
||||
|
||||
if (! outfn(arg, sc, priveleged))
|
||||
return (ENOMEM);
|
||||
carpr->carpr_count = 1;
|
||||
} else {
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
count++;
|
||||
|
||||
if (count > carpr->carpr_count)
|
||||
return (EMSGSIZE);
|
||||
|
||||
IFNET_FOREACH_CARP(ifp, sc) {
|
||||
if (! outfn(arg, sc, priveleged))
|
||||
return (ENOMEM);
|
||||
carpr->carpr_count = count;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
carp_ioctl(struct ifreq *ifr, u_long cmd, struct thread *td)
|
||||
{
|
||||
struct epoch_tracker et;
|
||||
struct carpreq carpr;
|
||||
struct ifnet *ifp;
|
||||
struct carp_softc *sc = NULL;
|
||||
int error = 0, locked = 0;
|
||||
int error = 0;
|
||||
|
||||
if ((error = copyin(ifr_data_get_ptr(ifr), &carpr, sizeof carpr)))
|
||||
return (error);
|
||||
|
||||
ifp = ifunit_ref(ifr->ifr_name);
|
||||
if (ifp == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
switch (ifp->if_type) {
|
||||
case IFT_ETHER:
|
||||
case IFT_L2VLAN:
|
||||
case IFT_BRIDGE:
|
||||
break;
|
||||
default:
|
||||
error = EOPNOTSUPP;
|
||||
if ((error = carp_is_supported_if(ifp)) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
|
||||
error = EADDRNOTAVAIL;
|
||||
|
@ -1761,136 +1895,27 @@ carp_ioctl(struct ifreq *ifr, u_long cmd, struct thread *td)
|
|||
case SIOCSVH:
|
||||
if ((error = priv_check(td, PRIV_NETINET_CARP)))
|
||||
break;
|
||||
if (carpr.carpr_vhid <= 0 || carpr.carpr_vhid > CARP_MAXVHID ||
|
||||
carpr.carpr_advbase < 0 || carpr.carpr_advskew < 0) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ifp->if_carp) {
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
if (sc->sc_vhid == carpr.carpr_vhid)
|
||||
break;
|
||||
}
|
||||
if (sc == NULL) {
|
||||
sc = carp_alloc(ifp);
|
||||
CARP_LOCK(sc);
|
||||
sc->sc_vhid = carpr.carpr_vhid;
|
||||
LLADDR(&sc->sc_addr)[0] = 0;
|
||||
LLADDR(&sc->sc_addr)[1] = 0;
|
||||
LLADDR(&sc->sc_addr)[2] = 0x5e;
|
||||
LLADDR(&sc->sc_addr)[3] = 0;
|
||||
LLADDR(&sc->sc_addr)[4] = 1;
|
||||
LLADDR(&sc->sc_addr)[5] = sc->sc_vhid;
|
||||
} else
|
||||
CARP_LOCK(sc);
|
||||
locked = 1;
|
||||
if (carpr.carpr_advbase > 0) {
|
||||
if (carpr.carpr_advbase > 255 ||
|
||||
carpr.carpr_advbase < CARP_DFLTINTV) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
sc->sc_advbase = carpr.carpr_advbase;
|
||||
}
|
||||
if (carpr.carpr_advskew >= 255) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
sc->sc_advskew = carpr.carpr_advskew;
|
||||
if (carpr.carpr_key[0] != '\0') {
|
||||
bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key));
|
||||
carp_hmac_prepare(sc);
|
||||
}
|
||||
if (sc->sc_state != INIT &&
|
||||
carpr.carpr_state != sc->sc_state) {
|
||||
switch (carpr.carpr_state) {
|
||||
case BACKUP:
|
||||
callout_stop(&sc->sc_ad_tmo);
|
||||
carp_set_state(sc, BACKUP,
|
||||
"user requested via ifconfig");
|
||||
carp_setrun(sc, 0);
|
||||
carp_delroute(sc);
|
||||
break;
|
||||
case MASTER:
|
||||
NET_EPOCH_ENTER(et);
|
||||
carp_master_down_locked(sc,
|
||||
"user requested via ifconfig");
|
||||
NET_EPOCH_EXIT(et);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
error = carp_ioctl_set(ifp, &carpr);
|
||||
break;
|
||||
|
||||
case SIOCGVH:
|
||||
{
|
||||
int priveleged;
|
||||
|
||||
if (carpr.carpr_vhid < 0 || carpr.carpr_vhid > CARP_MAXVHID) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (carpr.carpr_count < 1) {
|
||||
error = EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
if (ifp->if_carp == NULL) {
|
||||
error = ENOENT;
|
||||
break;
|
||||
}
|
||||
|
||||
priveleged = (priv_check(td, PRIV_NETINET_CARP) == 0);
|
||||
if (carpr.carpr_vhid != 0) {
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
if (sc->sc_vhid == carpr.carpr_vhid)
|
||||
break;
|
||||
if (sc == NULL) {
|
||||
error = ENOENT;
|
||||
break;
|
||||
}
|
||||
carp_carprcp(&carpr, sc, priveleged);
|
||||
error = copyout(&carpr, ifr_data_get_ptr(ifr),
|
||||
sizeof(carpr));
|
||||
} else {
|
||||
int i, count;
|
||||
|
||||
count = 0;
|
||||
IFNET_FOREACH_CARP(ifp, sc)
|
||||
count++;
|
||||
|
||||
if (count > carpr.carpr_count) {
|
||||
CIF_UNLOCK(ifp->if_carp);
|
||||
error = EMSGSIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
IFNET_FOREACH_CARP(ifp, sc) {
|
||||
carp_carprcp(&carpr, sc, priveleged);
|
||||
carpr.carpr_count = count;
|
||||
error = copyout(&carpr,
|
||||
(char *)ifr_data_get_ptr(ifr) +
|
||||
(i * sizeof(carpr)), sizeof(carpr));
|
||||
if (error) {
|
||||
CIF_UNLOCK(ifp->if_carp);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
error = carp_ioctl_get(ifp, td->td_ucred, &carpr,
|
||||
carp_carprcp, &carpr);
|
||||
if (error == 0) {
|
||||
error = copyout(&carpr,
|
||||
(char *)ifr_data_get_ptr(ifr),
|
||||
carpr.carpr_count * sizeof(carpr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error = EINVAL;
|
||||
}
|
||||
sx_xunlock(&carp_sx);
|
||||
|
||||
out:
|
||||
if (locked)
|
||||
CARP_UNLOCK(sc);
|
||||
if_rele(ifp);
|
||||
if (ifp != NULL)
|
||||
if_rele(ifp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
@ -2173,10 +2198,238 @@ carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nlattr_get_carp_key(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
|
||||
{
|
||||
if (__predict_false(NLA_DATA_LEN(nla) > CARP_KEY_LEN))
|
||||
return (EINVAL);
|
||||
|
||||
memcpy(target, NLA_DATA_CONST(nla), NLA_DATA_LEN(nla));
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct carp_nl_send_args {
|
||||
struct nlmsghdr *hdr;
|
||||
struct nl_pstate *npt;
|
||||
};
|
||||
|
||||
static bool
|
||||
carp_nl_send(void *arg, struct carp_softc *sc, int priv)
|
||||
{
|
||||
struct carp_nl_send_args *nlsa = arg;
|
||||
struct nlmsghdr *hdr = nlsa->hdr;
|
||||
struct nl_pstate *npt = nlsa->npt;
|
||||
struct nl_writer *nw = npt->nw;
|
||||
struct genlmsghdr *ghdr_new;
|
||||
|
||||
if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
|
||||
nlmsg_abort(nw);
|
||||
return (false);
|
||||
}
|
||||
|
||||
ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
|
||||
if (ghdr_new == NULL) {
|
||||
nlmsg_abort(nw);
|
||||
return (false);
|
||||
}
|
||||
|
||||
ghdr_new->cmd = CARP_NL_CMD_GET;
|
||||
ghdr_new->version = 0;
|
||||
ghdr_new->reserved = 0;
|
||||
|
||||
CARP_LOCK(sc);
|
||||
|
||||
nlattr_add_u32(nw, CARP_NL_VHID, sc->sc_vhid);
|
||||
nlattr_add_u32(nw, CARP_NL_STATE, sc->sc_state);
|
||||
nlattr_add_s32(nw, CARP_NL_ADVBASE, sc->sc_advbase);
|
||||
nlattr_add_s32(nw, CARP_NL_ADVSKEW, sc->sc_advskew);
|
||||
|
||||
if (priv)
|
||||
nlattr_add(nw, CARP_NL_KEY, sizeof(sc->sc_key), sc->sc_key);
|
||||
|
||||
CARP_UNLOCK(sc);
|
||||
|
||||
if (! nlmsg_end(nw)) {
|
||||
nlmsg_abort(nw);
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
struct nl_carp_parsed {
|
||||
unsigned int ifindex;
|
||||
uint32_t state;
|
||||
uint32_t vhid;
|
||||
int32_t advbase;
|
||||
int32_t advskew;
|
||||
char key[CARP_KEY_LEN];
|
||||
};
|
||||
|
||||
#define _IN(_field) offsetof(struct genlmsghdr, _field)
|
||||
#define _OUT(_field) offsetof(struct nl_carp_parsed, _field)
|
||||
|
||||
static const struct nlattr_parser nla_p_set[] = {
|
||||
{ .type = CARP_NL_VHID, .off = _OUT(vhid), .cb = nlattr_get_uint32 },
|
||||
{ .type = CARP_NL_STATE, .off = _OUT(state), .cb = nlattr_get_uint32 },
|
||||
{ .type = CARP_NL_ADVBASE, .off = _OUT(advbase), .cb = nlattr_get_uint32 },
|
||||
{ .type = CARP_NL_ADVSKEW, .off = _OUT(advskew), .cb = nlattr_get_uint32 },
|
||||
{ .type = CARP_NL_KEY, .off = _OUT(key), .cb = nlattr_get_carp_key },
|
||||
{ .type = CARP_NL_IFINDEX, .off = _OUT(ifindex), .cb = nlattr_get_uint32 },
|
||||
};
|
||||
static const struct nlfield_parser nlf_p_set[] = {
|
||||
};
|
||||
NL_DECLARE_PARSER(carp_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
|
||||
#undef _IN
|
||||
#undef _OUT
|
||||
|
||||
|
||||
static int
|
||||
carp_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
|
||||
{
|
||||
struct nl_carp_parsed attrs = { };
|
||||
struct carp_nl_send_args args;
|
||||
struct carpreq carpr = { };
|
||||
struct epoch_tracker et;
|
||||
if_t ifp;
|
||||
int error;
|
||||
|
||||
error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
NET_EPOCH_ENTER(et);
|
||||
ifp = ifnet_byindex_ref(attrs.ifindex);
|
||||
NET_EPOCH_EXIT(et);
|
||||
|
||||
if ((error = carp_is_supported_if(ifp)) != 0)
|
||||
goto out;
|
||||
|
||||
hdr->nlmsg_flags |= NLM_F_MULTI;
|
||||
args.hdr = hdr;
|
||||
args.npt = npt;
|
||||
|
||||
carpr.carpr_vhid = attrs.vhid;
|
||||
carpr.carpr_count = CARP_MAXVHID;
|
||||
|
||||
sx_xlock(&carp_sx);
|
||||
error = carp_ioctl_get(ifp, nlp_get_cred(npt->nlp), &carpr,
|
||||
carp_nl_send, &args);
|
||||
sx_xunlock(&carp_sx);
|
||||
|
||||
if (! nlmsg_end_dump(npt->nw, error, hdr))
|
||||
error = ENOMEM;
|
||||
|
||||
out:
|
||||
if (ifp != NULL)
|
||||
if_rele(ifp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
carp_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
|
||||
{
|
||||
struct nl_carp_parsed attrs = { };
|
||||
struct carpreq carpr;
|
||||
struct epoch_tracker et;
|
||||
if_t ifp;
|
||||
int error;
|
||||
|
||||
error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
if (attrs.vhid <= 0 || attrs.vhid > CARP_MAXVHID)
|
||||
return (EINVAL);
|
||||
if (attrs.state > CARP_MAXSTATE)
|
||||
return (EINVAL);
|
||||
if (attrs.advbase < 0 || attrs.advskew < 0)
|
||||
return (EINVAL);
|
||||
if (attrs.advbase > 255)
|
||||
return (EINVAL);
|
||||
if (attrs.advskew >= 255)
|
||||
return (EINVAL);
|
||||
|
||||
NET_EPOCH_ENTER(et);
|
||||
ifp = ifnet_byindex_ref(attrs.ifindex);
|
||||
NET_EPOCH_EXIT(et);
|
||||
|
||||
if ((error = carp_is_supported_if(ifp)) != 0)
|
||||
goto out;
|
||||
|
||||
if ((ifp->if_flags & IFF_MULTICAST) == 0) {
|
||||
error = EADDRNOTAVAIL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
carpr.carpr_count = 1;
|
||||
carpr.carpr_vhid = attrs.vhid;
|
||||
carpr.carpr_state = attrs.state;
|
||||
carpr.carpr_advbase = attrs.advbase;
|
||||
carpr.carpr_advskew = attrs.advskew;
|
||||
memcpy(&carpr.carpr_key, &attrs.key, sizeof(attrs.key));
|
||||
|
||||
sx_xlock(&carp_sx);
|
||||
error = carp_ioctl_set(ifp, &carpr);
|
||||
sx_xunlock(&carp_sx);
|
||||
|
||||
out:
|
||||
if (ifp != NULL)
|
||||
if_rele(ifp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static const struct nlhdr_parser *all_parsers[] = {
|
||||
&carp_parser
|
||||
};
|
||||
|
||||
static const struct genl_cmd carp_cmds[] = {
|
||||
{
|
||||
.cmd_num = CARP_NL_CMD_GET,
|
||||
.cmd_name = "SIOCGVH",
|
||||
.cmd_cb = carp_nl_get,
|
||||
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP |
|
||||
GENL_CMD_CAP_HASPOL,
|
||||
},
|
||||
{
|
||||
.cmd_num = CARP_NL_CMD_SET,
|
||||
.cmd_name = "SIOCSVH",
|
||||
.cmd_cb = carp_nl_set,
|
||||
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
|
||||
.cmd_priv = PRIV_NETINET_CARP,
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
carp_nl_register(void)
|
||||
{
|
||||
bool ret __diagused;
|
||||
int family_id __diagused;
|
||||
|
||||
NL_VERIFY_PARSERS(all_parsers);
|
||||
family_id = genl_register_family(CARP_NL_FAMILY_NAME, 0, 2,
|
||||
CARP_NL_CMD_MAX);
|
||||
MPASS(family_id != 0);
|
||||
|
||||
ret = genl_register_cmds(CARP_NL_FAMILY_NAME, carp_cmds,
|
||||
NL_ARRAY_LEN(carp_cmds));
|
||||
MPASS(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
carp_nl_unregister(void)
|
||||
{
|
||||
genl_unregister_family(CARP_NL_FAMILY_NAME);
|
||||
}
|
||||
|
||||
static void
|
||||
carp_mod_cleanup(void)
|
||||
{
|
||||
|
||||
carp_nl_unregister();
|
||||
|
||||
#ifdef INET
|
||||
(void)ipproto_unregister(IPPROTO_CARP);
|
||||
carp_iamatch_p = NULL;
|
||||
|
@ -2246,6 +2499,9 @@ carp_mod_load(void)
|
|||
return (err);
|
||||
}
|
||||
#endif
|
||||
|
||||
carp_nl_register();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
34
sys/netinet/ip_carp_nl.h
Normal file
34
sys/netinet/ip_carp_nl.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef _IP_CARP_NL_H
|
||||
#define _IP_CARP_NL_H
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#include <netinet/ip_carp.h>
|
||||
#include <netlink/netlink_generic.h>
|
||||
|
||||
/*
|
||||
* Netlink interface to carp(4).
|
||||
*/
|
||||
|
||||
#define CARP_NL_FAMILY_NAME "carp"
|
||||
|
||||
/* commands */
|
||||
enum {
|
||||
CARP_NL_CMD_UNSPEC = 0,
|
||||
CARP_NL_CMD_GET = 1,
|
||||
CARP_NL_CMD_SET = 2,
|
||||
__CARP_NL_CMD_MAX,
|
||||
};
|
||||
#define CARP_NL_CMD_MAX (__CARP_NL_CMD_MAX - 1)
|
||||
|
||||
enum carp_nl_type_t {
|
||||
CARP_NL_UNSPEC,
|
||||
CARP_NL_VHID = 1, /* u32 */
|
||||
CARP_NL_STATE = 2, /* u32 */
|
||||
CARP_NL_ADVBASE = 3, /* s32 */
|
||||
CARP_NL_ADVSKEW = 4, /* s32 */
|
||||
CARP_NL_KEY = 5, /* byte array */
|
||||
CARP_NL_IFINDEX = 6, /* u32 */
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue