Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142022320
D39048.id118807.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D39048.id118807.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_route.h>
#include <string.h>
#include <strings.h>
@@ -40,21 +46,250 @@
#include "libifconfig.h"
#include "libifconfig_internal.h"
+#include <stdio.h>
+
+struct getfamily_attrs {
+ uint32_t family_id;
+ char *family_name;
+};
+
+#define _IN(_field) offsetof(struct genlmsghdr, _field)
+#define _OUT(_field) offsetof(struct getfamily_attrs, _field)
+static struct snl_attr_parser ap_ctrl[] = {
+ { .type = CTRL_ATTR_FAMILY_ID , .off = _OUT(family_id), .cb = snl_attr_get_uint16 },
+ { .type = CTRL_ATTR_FAMILY_NAME, .off = _OUT(family_name), .cb = snl_attr_get_string },
+};
+static struct snl_field_parser fp_ctrl[] = {
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(genl_family_parser, struct genlmsghdr, fp_ctrl, ap_ctrl);
+
+
+static uint16_t
+snl_get_genl_family(ifconfig_handle_t *ih, struct snl_state *ss, const char *family_name)
+{
+ struct {
+ struct nlmsghdr hdr;
+ struct genlmsghdr ghdr;
+ char data[64];
+ } h = {
+ .hdr.nlmsg_type = GENL_ID_CTRL,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .hdr.nlmsg_seq = snl_get_seq(ss),
+ .ghdr.cmd = CTRL_CMD_GETFAMILY,
+ };
+
+ struct nlattr *nla = (struct nlattr *)&h.data[0];
+ int l = strlen(family_name);
+ strlcpy(NLA_DATA(nla), family_name, l + 1);
+ nla->nla_len = 4 + l + 1;
+ nla->nla_type = CTRL_ATTR_FAMILY_NAME;
+
+ h.hdr.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr);
+ h.hdr.nlmsg_len += NLMSG_ALIGN(nla->nla_len);
+
+ snl_send(ss, &h, NLMSG_ALIGN(h.hdr.nlmsg_len));
+
+ struct nlmsghdr *hdr = snl_read_message(ss);
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *nlerr = (struct nlmsgerr *)(hdr + 1);
+ ifconfig_error(ih, NETLINK, nlerr->error);
+ return (0);
+ }
+
+ struct getfamily_attrs attrs = {};
+ if (!snl_parse_nlmsg(ss, hdr, &genl_family_parser, &attrs)) {
+ ifconfig_error(ih, NETLINK, EINVAL);
+ return (0);
+ }
+
+ hdr = snl_read_message(ss); // XXX???
+ return (attrs.family_id);
+}
+
+#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 },
+};
+
+static struct snl_field_parser fp_carp_get[] = {
+};
+
+SNL_DECLARE_PARSER(carp_get_parser, struct genlmsghdr, fp_carp_get, 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_writer nw;
+ struct genlmsghdr *req;
+ struct nlmsghdr *hdr;
+ size_t i = 0;
+ 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(h, &ss, CARP_NL_FAMILY_NAME);
+ if (family_id == 0)
+ goto out;
+
+ hdr = snl_create_msg_request(&nw, family_id);
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+
+ req = snl_reserve_msg_object(&nw, struct genlmsghdr);
+ req->cmd = CARP_NL_CMD_GET;
+
+ snl_add_msg_attr_u32(&nw, CARP_NL_IFINDEX, ifindex);
+
+ if (vhid != 0) {
+ if (! snl_add_msg_attr_u32(&nw, CARP_NL_VHID, vhid)) {
+ ifconfig_error(h, NETLINK, ENOMEM);
+ goto out;
+ }
+ }
+ hdr = snl_finalize_msg(&nw);
+ if (hdr == NULL) {
+ ifconfig_error(h, NETLINK, ENOMEM);
+ goto out;
+ }
+ if (! snl_send(&ss, hdr, NLMSG_ALIGN(hdr->nlmsg_len))) {
+ ifconfig_error(h, NETLINK, EIO);
+ goto out;
+ }
+
+ while (true) {
+ hdr = snl_read_message(&ss);
+
+ if (hdr->nlmsg_type == NLMSG_DONE) {
+ break;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *nlerr = (struct nlmsgerr *)(hdr + 1);
+ ifconfig_error(h, NETLINK, nlerr->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 genlmsghdr *req;
+ struct nlmsghdr *hdr;
+ 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);
+ }
- 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(h, &ss, CARP_NL_FAMILY_NAME);
+ if (family_id == 0)
return (-1);
+
+ hdr = snl_create_msg_request(&nw, family_id);
+
+ req = snl_reserve_msg_object(&nw, struct genlmsghdr);
+ req->cmd = 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_i32(&nw, CARP_NL_ADVBASE, carpr->carpr_advbase);
+ snl_add_msg_attr_i32(&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);
+ if (! snl_send(&ss, hdr, NLMSG_ALIGN(hdr->nlmsg_len))) {
+ ifconfig_error(h, NETLINK, EIO);
+ goto out;
+ }
+
+ hdr = snl_read_message(&ss);
+ if (hdr->nlmsg_type == NLMSG_DONE)
+ goto out;
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *nlerr = (struct nlmsgerr *)(hdr + 1);
+ ifconfig_error(h, NETLINK, nlerr->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,12 @@
#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 <netlink/netlink_var.h>
+
#include <crypto/sha1.h>
static MALLOC_DEFINE(M_CARP, "CARP", "CARP addresses");
@@ -1709,9 +1716,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,6 +1731,126 @@
else
bzero(carpr->carpr_key, sizeof(carpr->carpr_key));
CARP_UNLOCK(sc);
+
+ return (true);
+}
+
+static int
+carp_ioctl_set(if_t ifp, struct ucred *cred, struct carpreq *carpr)
+{
+ struct carp_softc *sc = NULL;
+ int error = 0;
+
+ if ((error = priv_check_cred(cred, PRIV_NETINET_CARP)))
+ return (error);
+
+ 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:
+ carp_master_down_locked(sc,
+ "user requested via ifconfig");
+ 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
@@ -1730,8 +1858,7 @@
{
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);
@@ -1758,135 +1885,24 @@
sx_xlock(&carp_sx);
switch (cmd) {
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:
- carp_master_down_locked(sc,
- "user requested via ifconfig");
- break;
- default:
- break;
- }
- }
+ error = carp_ioctl_set(ifp, td->td_ucred, &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);
return (error);
@@ -2170,10 +2186,267 @@
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 nl_carp_parsed_get {
+ unsigned int ifindex;
+ uint32_t vhid;
+};
+
+#define _IN(_field) offsetof(struct genlmsghdr, _field)
+#define _OUT(_field) offsetof(struct nl_carp_parsed_get, _field)
+
+static const struct nlattr_parser nla_p_get[] = {
+ { .type = CARP_NL_VHID, .off = _OUT(vhid), .cb = nlattr_get_uint32 },
+ { .type = CARP_NL_IFINDEX, .off = _OUT(ifindex), .cb = nlattr_get_uint32 },
+};
+static const struct nlfield_parser nlf_p_get[] = {
+};
+NL_DECLARE_PARSER(carp_get_parser, struct genlmsghdr, nlf_p_get, nla_p_get);
+#undef _IN
+#undef _OUT
+
+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);
+}
+
+static int
+carp_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+ struct nl_carp_parsed_get 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_get_parser, npt, &attrs);
+ if (error != 0)
+ return (error);
+
+ NET_EPOCH_ENTER(et);
+ ifp = ifnet_byindex_ref(attrs.ifindex);
+ NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (ENXIO);
+
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_L2VLAN:
+ case IFT_BRIDGE:
+ break;
+ default:
+ error = EOPNOTSUPP;
+ goto out;
+ }
+
+ 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, npt->nlp->nl_cred, &carpr, carp_nl_send,
+ &args);
+ sx_xunlock(&carp_sx);
+
+out:
+ if_rele(ifp);
+
+ return (error);
+}
+
+struct nl_carp_parsed_set {
+ 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_set, _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_set_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
+#undef _IN
+#undef _OUT
+
+static int
+carp_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+ struct nl_carp_parsed_set attrs = { };
+ struct carpreq carpr;
+ struct epoch_tracker et;
+ if_t ifp;
+ int error;
+
+ error = nl_parse_nlmsg(hdr, &carp_set_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 (ifp == NULL)
+ return (ENXIO);
+
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_L2VLAN:
+ case IFT_BRIDGE:
+ break;
+ default:
+ error = EOPNOTSUPP;
+ 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, npt->nlp->nl_cred, &carpr);
+ sx_xunlock(&carp_sx);
+
+out:
+ if_rele(ifp);
+
+ return (error);
+}
+
+static const struct nlhdr_parser *all_parsers[] = {
+ &carp_get_parser,
+ &carp_set_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,
+ },
+};
+
+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;
@@ -2243,6 +2516,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
diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -450,6 +450,17 @@
return (false);
}
+static inline bool
+snl_attr_get_int32(struct snl_state *ss __unused, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ if (NLA_DATA_LEN(nla) == sizeof(int32_t)) {
+ *((uint32_t *)target) = *((const int32_t *)NLA_DATA_CONST(nla));
+ return (true);
+ }
+ return (false);
+}
+
static inline bool
snl_attr_get_uint32(struct snl_state *ss __unused, struct nlattr *nla,
const void *arg __unused, void *target)
@@ -720,6 +731,12 @@
return (snl_add_msg_attr(nw, attrtype, sizeof(uint32_t), &value));
}
+static inline bool
+snl_add_msg_attr_i32(struct snl_writer *nw, int attrtype, int32_t value)
+{
+ return (snl_add_msg_attr(nw, attrtype, sizeof(int32_t), &value));
+}
+
static inline bool
snl_add_msg_attr_u64(struct snl_writer *nw, int attrtype, uint64_t value)
{
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 16, 1:40 AM (2 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27656726
Default Alt Text
D39048.id118807.diff (25 KB)
Attached To
Mode
D39048: carp: add netlink interface
Attached
Detach File
Event Timeline
Log In to Comment