From nobody Mon Mar 20 10:18:02 2023 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Pg9ff2bqmz40WpG; Mon, 20 Mar 2023 10:18:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Pg9ff2Pgvz4L09; Mon, 20 Mar 2023 10:18:02 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1679307482; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=nKaEm93BRQtdJ1+Uo5kFWe6EWlXiVJHBRkTSDyC7xmU=; b=gkw7SdbtJDvZ6PbcCi2UiTG/d7x/oeErizr2MhC2q/WL2sM8vlSGBNQgHgDgDIqkWbFKFo YaLCx8tQkMJz5YgOEIIQNr/k1tZyQDk6NqixSgjmJQ2KNi7jmKft72x0oORbIkaYpcs+XV lkfHkVzjU9lmIQVKa6U/AAc61nGGyGoaK4DnsEiyISD0JdqXo8XpU/D4hyc34UE9QJhrJL mj/VwhKIjR3v9xo+25bFYE+2nXhYLEVJ1mDYHSacmOJwPAjTdrNyrLVBcccViwN4uek1l/ E1IQxERP2E/UfFjeyzFLvIJnioIxc9UC5RVTbNpGH+TeVumnv8e9Rui121oUxQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1679307482; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=nKaEm93BRQtdJ1+Uo5kFWe6EWlXiVJHBRkTSDyC7xmU=; b=lVLgIHFeVADRzQ953h63LFrNrzHMYbJr0oG9qoKa+60zsO9PHV2vmnUy7dCalb7sBnvJGX gZ4oVUSxmYyykxrL8vaNSk9NvB/1eYLAhl7DFHPkTiSCHNMLCzWZbnMhBJDa6sDBps5i01 abKtiQaDCu3lyQuF4BNLGMW0YjcVqK6dc+t3iDYnr6fspytdXJqttnbyW7OGzGkgb2nUbD DCEMo/UpgGExW/PPHdT6BJz2OXfW1A+M+Lz2llzPj6JU05FpYT8pZudF0jGl2VgaDe/BdG roBhPUD/ULjTYpYhpumPI1SylYQpnJd3sp4fMIJKNcjb3UxNzvKxVkBpwK2mIg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1679307482; a=rsa-sha256; cv=none; b=SmZ3S/eHJeqg5/bnTpUEzCP6VNxf6x3c6zHgi1teU/UO4XUR9YPAvPca+zPb6kQWA5mpAA IQB4bZ8okqcVSBytLC5q00J1F3wK8+gkCUC9HVli3acbWFMiGCqPDd5Hqdw+UIeQfqzKkH W4fXCAfe9Dty2fEb04OVNsTwP3KkfxF6ixo6gCdpjBUMatr1rm/zf8jA58nQThCsodVYV+ Z7doDctsRiYc3DcaxM7SaXK4L7r2DD8nVK62xVhN993xwApyKrdmd1elHwWjaTrm60PhOh ErsROAOMM43AkPzfqIH8DzydBjqwC2mSc9tIIy/Vsod2RT4wCutIcRjCaIS2TQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Pg9ff1SjQzQwl; Mon, 20 Mar 2023 10:18:02 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 32KAI2Zi046617; Mon, 20 Mar 2023 10:18:02 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 32KAI2sr046616; Mon, 20 Mar 2023 10:18:02 GMT (envelope-from git) Date: Mon, 20 Mar 2023 10:18:02 GMT Message-Id: <202303201018.32KAI2sr046616@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: 40e043596409 - main - carp: add netlink interface List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 40e0435964094dfda21089d9989197999c06c4bc Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=40e0435964094dfda21089d9989197999c06c4bc commit 40e0435964094dfda21089d9989197999c06c4bc Author: Kristof Provost AuthorDate: 2023-03-07 18:17:09 +0000 Commit: Kristof Provost CommitDate: 2023-03-20 09:52:27 +0000 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 --- lib/libifconfig/libifconfig.h | 20 +- lib/libifconfig/libifconfig_carp.c | 177 ++++++++++- lib/libifconfig/libifconfig_internal.c | 14 + lib/libifconfig/libifconfig_internal.h | 3 + sbin/ifconfig/carp.c | 18 +- sys/netinet/ip_carp.c | 524 ++++++++++++++++++++++++--------- sys/netinet/ip_carp_nl.h | 34 +++ 7 files changed, 634 insertions(+), 156 deletions(-) diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h index c8bbb5edd3bb..64a61af0708c 100644 --- a/lib/libifconfig/libifconfig.h +++ b/lib/libifconfig/libifconfig.h @@ -33,6 +33,7 @@ #include #include +#include #include #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 diff --git a/lib/libifconfig/libifconfig_carp.c b/lib/libifconfig/libifconfig_carp.c index cd31a85dfd9a..ffc497590aaa 100644 --- a/lib/libifconfig/libifconfig_carp.c +++ b/lib/libifconfig/libifconfig_carp.c @@ -33,6 +33,12 @@ #include #include +#include + +#include +#include +#include +#include #include #include @@ -40,21 +46,174 @@ #include "libifconfig.h" #include "libifconfig_internal.h" +#include + +#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.c b/lib/libifconfig/libifconfig_internal.c index 6b7f767e23e0..7d40a30e07cb 100644 --- a/lib/libifconfig/libifconfig_internal.c +++ b/lib/libifconfig/libifconfig_internal.c @@ -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; +} diff --git a/lib/libifconfig/libifconfig_internal.h b/lib/libifconfig/libifconfig_internal.h index 77add7574fa8..0096f7ca94ea 100644 --- a/lib/libifconfig/libifconfig_internal.h +++ b/lib/libifconfig/libifconfig_internal.h @@ -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); diff --git a/sbin/ifconfig/carp.c b/sbin/ifconfig/carp.c index 23a119e3b9d7..3fa6f3ac269d 100644 --- a/sbin/ifconfig/carp.c +++ b/sbin/ifconfig/carp.c @@ -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 = { }; - 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 @@ 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"); } diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 1b8b10b06168..78bc6d312abb 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #endif @@ -84,6 +85,11 @@ __FBSDID("$FreeBSD$"); #include #endif +#include +#include +#include +#include + #include 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); } diff --git a/sys/netinet/ip_carp_nl.h b/sys/netinet/ip_carp_nl.h new file mode 100644 index 000000000000..ffb7c1ef5aae --- /dev/null +++ b/sys/netinet/ip_carp_nl.h @@ -0,0 +1,34 @@ +#ifndef _IP_CARP_NL_H +#define _IP_CARP_NL_H + +#include + +#include +#include *** 27 LINES SKIPPED ***