Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102670046
D39048.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D39048.diff
View Options
diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h
--- a/lib/libifconfig/libifconfig.h
+++ b/lib/libifconfig/libifconfig.h
@@ -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 @@
OK = 0,
OTHER,
IOCTL,
- SOCKET
+ SOCKET,
+ NETLINK
} ifconfig_errtype;
/*
@@ -51,7 +53,6 @@
struct ifconfig_handle;
typedef struct ifconfig_handle ifconfig_handle_t;
-struct carpreq;
struct ifaddrs;
struct ifbropreq;
struct ifbreq;
@@ -279,8 +280,21 @@
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
diff --git a/lib/libifconfig/libifconfig_carp.c b/lib/libifconfig/libifconfig_carp.c
--- a/lib/libifconfig/libifconfig_carp.c
+++ b/lib/libifconfig/libifconfig_carp.c
@@ -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>
+
+#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 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;
+
+ 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);
+ 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_get_info(ifconfig_handle_t *h, const char *name,
- struct carpreq *carpr, int ncarpr)
+ifconfig_carp_set_info(ifconfig_handle_t *h, const char *name,
+ const struct ifconfig_carp *carpr)
{
- struct ifreq ifr;
+ 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);
+ }
- 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;
+ snl_init_writer(&ss, &nw);
- if (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGVH, &ifr) != 0) {
+ 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;
+ }
- return (0);
+ 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));
}
diff --git a/lib/libifconfig/libifconfig_internal.h b/lib/libifconfig/libifconfig_internal.h
--- a/lib/libifconfig/libifconfig_internal.h
+++ b/lib/libifconfig/libifconfig_internal.h
@@ -83,3 +83,6 @@
/** 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);
diff --git a/lib/libifconfig/libifconfig_internal.c b/lib/libifconfig/libifconfig_internal.c
--- a/lib/libifconfig/libifconfig_internal.c
+++ b/lib/libifconfig/libifconfig_internal.c
@@ -102,3 +102,17 @@
*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;
+}
diff --git a/sbin/ifconfig/carp.c b/sbin/ifconfig/carp.c
--- a/sbin/ifconfig/carp.c
+++ b/sbin/ifconfig/carp.c
@@ -72,7 +72,7 @@
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 @@
static void
setcarp_callback(int s, void *arg __unused)
{
- struct carpreq carpr;
+ struct ifconfig_carp carpr = { };
- 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 (ifconfig_carp_get_vhid(lifh, name, &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);
@@ -149,7 +147,7 @@
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");
}
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -67,6 +67,7 @@
#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 @@
#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 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 @@
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 @@
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 @@
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 @@
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 @@
return (err);
}
#endif
+
+ carp_nl_register();
+
return (0);
}
diff --git a/sys/netinet/ip_carp_nl.h b/sys/netinet/ip_carp_nl.h
new file mode 100644
--- /dev/null
+++ b/sys/netinet/ip_carp_nl.h
@@ -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
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 16, 3:27 PM (21 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14660687
Default Alt Text
D39048.diff (22 KB)
Attached To
Mode
D39048: carp: add netlink interface
Attached
Detach File
Event Timeline
Log In to Comment