From nobody Mon Jan 23 22:12:06 2023 X-Original-To: dev-commits-src-branches@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 4P148Q2YBlz2t0nJ; Mon, 23 Jan 2023 22:12:06 +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 4P148Q23qKz3LfT; Mon, 23 Jan 2023 22:12:06 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1674511926; 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=4lkWp1AjgxNB7AP8fCtq+/2ObeEWXfnUJm8jo7STV1c=; b=JEA+xBeh656y1fkMCVCyKlVVE4ANhzT8nmi8HSpxuu/8FRb9kHkIvMyglMVNXRh8SwOLMN RGI7P/xzcchxw1eM890M6fOq/YkTeeaFNt4cXpCKGL1X8w1sBVMVtCIydiVhRbHjA625I5 44JMlpF4NCFkl7Ra9baTSkakFYRUO/XqDiMkye940YvkoHrx3px33a4DP7KUobTwERMeaZ //AHwgfTgFiPYPxp1X/AixfXQsBn+wO848xkgJ73ynNeIivrlf3YXljwm+BxPWIri3yO0x 6zAioAX8k8IS0o7NvgM9Gl3PR9ZRagsHgkMkmB0bwyuIP/+6PfVYrOPsJELRVw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1674511926; 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=4lkWp1AjgxNB7AP8fCtq+/2ObeEWXfnUJm8jo7STV1c=; b=yHVrBwF0g9fCV5IFQjrNYEQ0LkODemxL5DLQ2/C5SKgRCMmJBjpYGCaxyGqtYpZUJh+VB3 rRqm1bjuNZlj2w0eUkVsBzjOtlazc7MRDf+9VKoETJyBdqLG45E1DA71c7eaTt4qCuu+5J vbJQ2EHv+gIfcyIXXRfurYUVRUXIQpB8GIVBR3VE7FIB4nPr3yVbV5vWW2OWmTHpk4JN6w nQb+vTLEwKeulZt20X4ix9LRL0ptERu4SGoGH+pi7XWL4mvFVQaozEQ6/Dvj3Rs9Ddrvxo ox10wPm/pXtPCh8qQOOwmufbLaPJ75kyomGsTh4vHO23j4cSx6rvJMpcCPcPmA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1674511926; a=rsa-sha256; cv=none; b=W1Bgf/ZPSPJgmxTC5CdKMzqiPnBo3FNMGT3qme8QO3N2SMek1zGPRGfscs6Z/AEbnRiYVG 7xi1u/3hKrYx8hObkpXTfPjW2cSrogjpafpmAiZZyUOd+UzEWRKt6ex4yqGFe/9+KSthAb EZWIu2mfvmuzETUFzur+8O5uSM0cudelMCHWA174/XF2Aj5/+KtHeC+Nq4uNc3Hkg9JWr+ w23js8erQEOCoFBeyZN/V4uz9ISDTN/LZ4ErCoKX26zIbcSHmQ7lrxwHgV6tzeeF0iMYV+ r8B7uX28qDfmEYxVqmxw6pbxyMdsSn6CG3LHP4tgq1rJyK2ziBfCRXWu+ntEFg== 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 4P148Q18Yjzltm; Mon, 23 Jan 2023 22:12:06 +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 30NMC6je012982; Mon, 23 Jan 2023 22:12:06 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 30NMC6fe012980; Mon, 23 Jan 2023 22:12:06 GMT (envelope-from git) Date: Mon, 23 Jan 2023 22:12:06 GMT Message-Id: <202301232212.30NMC6fe012980@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: "Alexander V. Chernikov" Subject: git: db476bbc226a - stable/13 - netlink: improve interface handling List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: melifaro X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: db476bbc226a11e6948a53e3fb8e8ca6b192f409 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=db476bbc226a11e6948a53e3fb8e8ca6b192f409 commit db476bbc226a11e6948a53e3fb8e8ca6b192f409 Author: Alexander V. Chernikov AuthorDate: 2022-12-14 19:45:01 +0000 Commit: Alexander V. Chernikov CommitDate: 2023-01-23 22:04:03 +0000 netlink: improve interface handling * Separate interface creation from interface modification code * Support setting some interface attributes (ifdescr, mtu, up/down, promisc) * Improve interaction with the cloners requiring to parse/write custom interface attributes * Add bitmask-based way of checking if the attribute is present in the message * Don't use multipart RTM_GETLINK replies when searching for the specific interface names * Use ENODEV instead of ENOENT in case of failed RTM_GETLINK search * Add python netlink test helpers * Add some netlink interface tests Differential Revision: https://reviews.freebsd.org/D37668 (cherry picked from commit 80f03e63d67ede8fedbed4bd6bf6b12ec2ab2cfb) --- etc/mtree/BSD.tests.dist | 2 + share/man/man4/rtnetlink.4 | 2 + sys/netlink/netlink_message_parser.c | 20 +- sys/netlink/netlink_message_parser.h | 28 +- sys/netlink/route/iface.c | 208 ++++- sys/netlink/route/iface_drivers.c | 100 ++- sys/netlink/route/interface.h | 2 +- sys/netlink/route/route_var.h | 15 +- tests/atf_python/sys/net/Makefile | 2 +- tests/atf_python/sys/net/netlink.py | 1495 ++++++++++++++++++++++++++++++++++ tests/sys/netlink/Makefile | 14 + tests/sys/netlink/test_rtnl_iface.py | 281 +++++++ 12 files changed, 2116 insertions(+), 53 deletions(-) diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 475b9571d8e9..0dabe96110d8 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -820,6 +820,8 @@ tunnel .. .. + netlink + .. netmap .. netpfil diff --git a/share/man/man4/rtnetlink.4 b/share/man/man4/rtnetlink.4 index 9f20671719f0..a06807809691 100644 --- a/share/man/man4/rtnetlink.4 +++ b/share/man/man4/rtnetlink.4 @@ -309,6 +309,8 @@ IFLA_ALT_IFNAME interface name (binary) (readonly) Link-level broadcast address. .It Dv IFLA_IFNAME (string) New interface name. +.It Dv IFLA_IFALIAS +(string) Interface description. .It Dv IFLA_LINK (uint32_t) (readonly) Interface index. .It Dv IFLA_MASTER diff --git a/sys/netlink/netlink_message_parser.c b/sys/netlink/netlink_message_parser.c index d33eddb800e4..451d9d497491 100644 --- a/sys/netlink/netlink_message_parser.c +++ b/sys/netlink/netlink_message_parser.c @@ -147,17 +147,23 @@ nl_parse_attrs_raw(struct nlattr *nla_head, int len, const struct nlattr_parser return (0); } -int -nl_parse_attrs(struct nlmsghdr *hdr, int hdrlen, struct nlattr_parser *ps, int pslen, - struct nl_pstate *npt, void *target) +void +nl_get_attrs_bmask_raw(struct nlattr *nla_head, int len, struct nlattr_bmask *bm) { - int off = NLMSG_HDRLEN + NETLINK_ALIGN(hdrlen); - int len = hdr->nlmsg_len - off; - struct nlattr *nla_head = (struct nlattr *)((char *)hdr + off); + struct nlattr *nla = NULL; + + bzero(bm->mask, sizeof(bm->mask)); - return (nl_parse_attrs_raw(nla_head, len, ps, pslen, npt, target)); + NLA_FOREACH(nla, nla_head, len) { + if (nla->nla_len < sizeof(struct nlattr)) + return; + int nla_type = nla->nla_type & NLA_TYPE_MASK; + if (nla_type <= sizeof(bm->mask) * 8) + bm->mask[nla_type / 8] |= 1 << (nla_type % 8); + } } + int nlattr_get_flag(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target) { diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h index b23b223ef80e..96fd1c7337b7 100644 --- a/sys/netlink/netlink_message_parser.h +++ b/sys/netlink/netlink_message_parser.h @@ -152,15 +152,21 @@ static const struct nlhdr_parser _name = { \ .np_size = NL_ARRAY_LEN(_np), \ } -struct nlarr_hdr { - int num_items; - int max_items; +struct nlattr_bmask { + uint64_t mask[2]; }; +static inline bool +nl_has_attr(const struct nlattr_bmask *bm, unsigned int attr_type) +{ + MPASS(attr_type < sizeof(bm->mask) * 8); + + return ((bm->mask[attr_type / 8] & (1 << (attr_type % 8)))); +} +void nl_get_attrs_bmask_raw(struct nlattr *nla_head, int len, struct nlattr_bmask *bm); + int nl_parse_attrs_raw(struct nlattr *nla_head, int len, const struct nlattr_parser *ps, int pslen, struct nl_pstate *npt, void *target); -int nl_parse_attrs(struct nlmsghdr *hdr, int hdrlen, struct nlattr_parser *ps, - int pslen, struct nl_pstate *npt, void *target); int nlattr_get_flag(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target); @@ -270,5 +276,17 @@ nl_parse_nlmsg(struct nlmsghdr *hdr, const struct nlhdr_parser *parser, return (nl_parse_header(hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, npt, target)); } +static inline void +nl_get_attrs_bmask_nlmsg(struct nlmsghdr *hdr, const struct nlhdr_parser *parser, + struct nlattr_bmask *bm) +{ + struct nlattr *nla_head; + + nla_head = (struct nlattr *)((char *)(hdr + 1) + parser->nl_hdr_off); + int len = hdr->nlmsg_len - sizeof(*hdr) - parser->nl_hdr_off; + + nl_get_attrs_bmask_raw(nla_head, len, bm); +} + #endif #endif diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c index 579869e9662c..b033ba71009d 100644 --- a/sys/netlink/route/iface.c +++ b/sys/netlink/route/iface.c @@ -75,6 +75,8 @@ static SLIST_HEAD(, nl_cloner) nl_cloners = SLIST_HEAD_INITIALIZER(nl_cloners); static struct sx rtnl_cloner_lock; SX_SYSINIT(rtnl_cloner_lock, &rtnl_cloner_lock, "rtnl cloner lock"); +static struct nl_cloner *rtnl_iface_find_cloner_locked(const char *name); + /* * RTM_GETLINK request * sendto(3, {{len=32, type=RTM_GETLINK, flags=NLM_F_REQUEST|NLM_F_DUMP, seq=1641940952, pid=0}, @@ -286,11 +288,23 @@ dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr, nlattr_add_u32(nw, IFLA_MAX_MTU, 9000); nlattr_add_u32(nw, IFLA_GROUP, 0); */ + + if (ifp->if_description != NULL) + nlattr_add_string(nw, IFLA_IFALIAS, ifp->if_description); + get_stats(nw, ifp); uint32_t val = (ifp->if_flags & IFF_PROMISC) != 0; nlattr_add_u32(nw, IFLA_PROMISCUITY, val); + sx_slock(&rtnl_cloner_lock); + struct nl_cloner *cloner = rtnl_iface_find_cloner_locked(ifp->if_dname); + if (cloner != NULL && cloner->dump_f != NULL) { + /* Ignore any dump error */ + cloner->dump_f(ifp, nw); + } + sx_sunlock(&rtnl_cloner_lock); + if (nlmsg_end(nw)) return (true); @@ -320,6 +334,8 @@ check_ifmsg(void *hdr, struct nl_pstate *npt) static const struct nlfield_parser nlf_p_if[] = { { .off_in = _IN(ifi_type), .off_out = _OUT(ifi_type), .cb = nlf_get_u16 }, { .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = nlf_get_u32 }, + { .off_in = _IN(ifi_flags), .off_out = _OUT(ifi_flags), .cb = nlf_get_u32 }, + { .off_in = _IN(ifi_change), .off_out = _OUT(ifi_change), .cb = nlf_get_u32 }, }; static const struct nlattr_parser nla_p_linfo[] = { @@ -333,6 +349,7 @@ static const struct nlattr_parser nla_p_if[] = { { .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = nlattr_get_uint32 }, { .type = IFLA_LINK, .off = _OUT(ifi_index), .cb = nlattr_get_uint32 }, { .type = IFLA_LINKINFO, .arg = &linfo_parser, .cb = nlattr_get_nested }, + { .type = IFLA_IFALIAS, .off = _OUT(ifla_ifalias), .cb = nlattr_get_string }, { .type = IFLA_GROUP, .off = _OUT(ifla_group), .cb = nlattr_get_string }, { .type = IFLA_ALT_IFNAME, .off = _OUT(ifla_ifname), .cb = nlattr_get_string }, }; @@ -379,28 +396,39 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n .nw = npt->nw, .hdr.nlmsg_pid = hdr->nlmsg_pid, .hdr.nlmsg_seq = hdr->nlmsg_seq, - .hdr.nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI, + .hdr.nlmsg_flags = hdr->nlmsg_flags, .hdr.nlmsg_type = NL_RTM_NEWLINK, }; - /* Fast track for an interface w/ explicit index match */ - if (attrs.ifi_index != 0) { - NET_EPOCH_ENTER(et); - ifp = ifnet_byindex_ref(attrs.ifi_index); - NET_EPOCH_EXIT(et); - NLP_LOG(LOG_DEBUG3, nlp, "fast track -> searching index %u", attrs.ifi_index); + /* Fast track for an interface w/ explicit name or index match */ + if ((attrs.ifi_index != 0) || (attrs.ifla_ifname != NULL)) { + if (attrs.ifi_index != 0) { + NLP_LOG(LOG_DEBUG3, nlp, "fast track -> searching index %u", + attrs.ifi_index); + NET_EPOCH_ENTER(et); + ifp = ifnet_byindex_ref(attrs.ifi_index); + NET_EPOCH_EXIT(et); + } else { + NLP_LOG(LOG_DEBUG3, nlp, "fast track -> searching name %s", + attrs.ifla_ifname); + ifp = ifunit_ref(attrs.ifla_ifname); + } + if (ifp != NULL) { if (match_iface(&attrs, ifp)) { if (!dump_iface(wa.nw, ifp, &wa.hdr, 0)) error = ENOMEM; } else - error = ESRCH; + error = ENODEV; if_rele(ifp); } else - error = ESRCH; + error = ENODEV; return (error); } + /* Always treat non-direct-match as a multipart message */ + wa.hdr.nlmsg_flags |= NLM_F_MULTI; + /* * Fetching some link properties require performing ioctl's that may be blocking. * Address it by saving referenced pointers of the matching links, @@ -504,48 +532,146 @@ rtnl_handle_dellink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n return (error); } +/* + * New link: + * type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1668185590, pid=0}, + * {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0} + * [ + * {{nla_len=8, nla_type=IFLA_MTU}, 123}, + * {{nla_len=10, nla_type=IFLA_IFNAME}, "vlan1"}, + * {{nla_len=24, nla_type=IFLA_LINKINFO}, + * [ + * {{nla_len=8, nla_type=IFLA_INFO_KIND}, "vlan"...}, + * {{nla_len=12, nla_type=IFLA_INFO_DATA}, "\x06\x00\x01\x00\x7b\x00\x00\x00"}]}]} + * + * Update link: + * type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK, seq=1668185923, pid=0}, + * {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=if_nametoindex("lo"), ifi_flags=0, ifi_change=0}, + * {{nla_len=8, nla_type=IFLA_MTU}, 123}} + * + * + * Check command availability: + * type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK, seq=0, pid=0}, + * {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0} + */ + + static int -rtnl_handle_newlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt) +create_link(struct nlmsghdr *hdr, struct nl_parsed_link *lattrs, + struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt) { - struct nl_cloner *cloner; - int error; + if (lattrs->ifla_ifname == NULL || strlen(lattrs->ifla_ifname) == 0) { + NLMSG_REPORT_ERR_MSG(npt, "empty IFLA_IFNAME attribute"); + return (EINVAL); + } + if (lattrs->ifla_cloner == NULL || strlen(lattrs->ifla_cloner) == 0) { + NLMSG_REPORT_ERR_MSG(npt, "empty IFLA_INFO_KIND attribute"); + return (EINVAL); + } - struct nl_parsed_link attrs = {}; - error = nl_parse_nlmsg(hdr, &ifmsg_parser, npt, &attrs); - if (error != 0) - return (error); + bool found = false; + int error = 0; + + sx_slock(&rtnl_cloner_lock); + struct nl_cloner *cloner = rtnl_iface_find_cloner_locked(lattrs->ifla_cloner); + if (cloner != NULL) { + found = true; + error = cloner->create_f(lattrs, bm, nlp, npt); + } + sx_sunlock(&rtnl_cloner_lock); + + if (!found) + error = generic_cloner.create_f(lattrs, bm, nlp, npt); + + return (error); +} + +static int +modify_link(struct nlmsghdr *hdr, struct nl_parsed_link *lattrs, + struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt) +{ + struct ifnet *ifp = NULL; + struct epoch_tracker et; - if (attrs.ifla_ifname == NULL || strlen(attrs.ifla_ifname) == 0) { - /* Applications like ip(8) verify RTM_NEWLINK existance - * by calling it with empty arguments. Always return "innocent" - * error. + if (lattrs->ifi_index == 0 && lattrs->ifla_ifname == NULL) { + /* + * Applications like ip(8) verify RTM_NEWLINK command + * existence by calling it with empty arguments. Always + * return "innocent" error in that case. */ - NLMSG_REPORT_ERR_MSG(npt, "empty IFLA_IFNAME attribute"); + NLMSG_REPORT_ERR_MSG(npt, "empty ifi_index field"); return (EPERM); } - if (attrs.ifla_cloner == NULL || strlen(attrs.ifla_cloner) == 0) { - NLMSG_REPORT_ERR_MSG(npt, "empty IFLA_INFO_KIND attribute"); - return (EINVAL); + if (lattrs->ifi_index != 0) { + NET_EPOCH_ENTER(et); + ifp = ifnet_byindex_ref(lattrs->ifi_index); + NET_EPOCH_EXIT(et); + if (ifp == NULL) { + NLMSG_REPORT_ERR_MSG(npt, "unable to find interface #%u", + lattrs->ifi_index); + return (ENOENT); + } } - sx_slock(&rtnl_cloner_lock); - SLIST_FOREACH(cloner, &nl_cloners, next) { - if (!strcmp(attrs.ifla_cloner, cloner->name)) { - error = cloner->create_f(&attrs, nlp, npt); - sx_sunlock(&rtnl_cloner_lock); - return (error); + if (ifp == NULL && lattrs->ifla_ifname != NULL) { + ifp = ifunit_ref(lattrs->ifla_ifname); + if (ifp == NULL) { + NLMSG_REPORT_ERR_MSG(npt, "unable to find interface %s", + lattrs->ifla_ifname); + return (ENOENT); } } + + MPASS(ifp != NULL); + + /* + * There can be multiple kinds of interfaces: + * 1) cloned, with additional options + * 2) cloned, but w/o additional options + * 3) non-cloned (e.g. "physical). + * + * Thus, try to find cloner-specific callback and fallback to the + * "default" handler if not found. + */ + bool found = false; + int error = 0; + + sx_slock(&rtnl_cloner_lock); + struct nl_cloner *cloner = rtnl_iface_find_cloner_locked(ifp->if_dname); + if (cloner != NULL) { + found = true; + error = cloner->modify_f(ifp, lattrs, bm, nlp, npt); + } sx_sunlock(&rtnl_cloner_lock); - /* TODO: load cloner module if not exists & privilege permits */ - NLMSG_REPORT_ERR_MSG(npt, "interface type %s not supported", attrs.ifla_cloner); - return (ENOTSUP); + if (!found) + error = generic_cloner.modify_f(ifp, lattrs, bm, nlp, npt); + + if_rele(ifp); return (error); } + +static int +rtnl_handle_newlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt) +{ + struct nlattr_bmask bm; + int error; + + struct nl_parsed_link attrs = {}; + error = nl_parse_nlmsg(hdr, &ifmsg_parser, npt, &attrs); + if (error != 0) + return (error); + nl_get_attrs_bmask_nlmsg(hdr, &ifmsg_parser, &bm); + + if (hdr->nlmsg_flags & NLM_F_CREATE) + return (create_link(hdr, &attrs, &bm, nlp, npt)); + else + return (modify_link(hdr, &attrs, &bm, nlp, npt)); +} + /* {ifa_family=AF_INET, ifa_prefixlen=8, ifa_flags=IFA_F_PERMANENT, ifa_scope=RT_SCOPE_HOST, ifa_index=if_nametoindex("lo")}, @@ -863,13 +989,27 @@ rtnl_iface_add_cloner(struct nl_cloner *cloner) sx_xunlock(&rtnl_cloner_lock); } -void rtnl_iface_del_cloner(struct nl_cloner *cloner) +void +rtnl_iface_del_cloner(struct nl_cloner *cloner) { sx_xlock(&rtnl_cloner_lock); SLIST_REMOVE(&nl_cloners, cloner, nl_cloner, next); sx_xunlock(&rtnl_cloner_lock); } +static struct nl_cloner * +rtnl_iface_find_cloner_locked(const char *name) +{ + struct nl_cloner *cloner; + + SLIST_FOREACH(cloner, &nl_cloners, next) { + if (!strcmp(name, cloner->name)) + return (cloner); + } + + return (NULL); +} + void rtnl_ifaces_init(void) { diff --git a/sys/netlink/route/iface_drivers.c b/sys/netlink/route/iface_drivers.c index ccc8f2184fa3..7f098b808743 100644 --- a/sys/netlink/route/iface_drivers.c +++ b/sys/netlink/route/iface_drivers.c @@ -58,6 +58,95 @@ __FBSDID("$FreeBSD$"); #include _DECLARE_DEBUG(LOG_DEBUG); +/* + * Generic modification interface handler. + * Responsible for changing network stack interface attributes + * such as state, mtu or description. + */ +static int +modify_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs, + const struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt) +{ + int error; + + if (lattrs->ifla_ifalias != NULL) { + if (nlp_has_priv(nlp, PRIV_NET_SETIFDESCR)) { + int len = strlen(lattrs->ifla_ifalias) + 1; + char *buf = if_allocdescr(len, true); + + memcpy(buf, lattrs->ifla_ifalias, len); + if_setdescr(ifp, buf); + getmicrotime(&ifp->if_lastchange); + } else { + nlmsg_report_err_msg(npt, "Not enough privileges to set descr"); + return (EPERM); + } + } + + if ((lattrs->ifi_change & IFF_UP) && (lattrs->ifi_flags & IFF_UP) == 0) { + /* Request to down the interface */ + if_down(ifp); + } + + if (lattrs->ifla_mtu > 0) { + if (nlp_has_priv(nlp, PRIV_NET_SETIFMTU)) { + struct ifreq ifr = { .ifr_mtu = lattrs->ifla_mtu }; + error = ifhwioctl(SIOCSIFMTU, ifp, (char *)&ifr, curthread); + } else { + nlmsg_report_err_msg(npt, "Not enough privileges to set mtu"); + return (EPERM); + } + } + + if (lattrs->ifi_change & IFF_PROMISC) { + error = ifpromisc(ifp, lattrs->ifi_flags & IFF_PROMISC); + if (error != 0) { + nlmsg_report_err_msg(npt, "unable to set promisc"); + return (error); + } + } + + return (0); +} + +/* + * Generic creation interface handler. + * Responsible for creating interfaces w/o parameters and setting + * misc attributes such as state, mtu or description. + */ +static int +create_generic(struct nl_parsed_link *lattrs, const struct nlattr_bmask *bm, + struct nlpcb *nlp, struct nl_pstate *npt) +{ + int error = 0; + + struct ifc_data ifd = {}; + struct ifnet *ifp = NULL; + error = ifc_create_ifp(lattrs->ifla_ifname, &ifd, &ifp); + + NLP_LOG(LOG_DEBUG2, nlp, "clone for %s returned %d", lattrs->ifla_ifname, error); + + if (error == 0) { + struct epoch_tracker et; + + NET_EPOCH_ENTER(et); + bool success = if_try_ref(ifp); + NET_EPOCH_EXIT(et); + if (!success) + return (EINVAL); + error = modify_generic(ifp, lattrs, bm, nlp, npt); + if_rele(ifp); + } + + return (error); +} + +struct nl_cloner generic_cloner = { + .name = "_default_", + .create_f = create_generic, + .modify_f = modify_generic, +}; + /* * * {len=76, type=RTM_NEWLINK, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1662892737, pid=0}, @@ -87,7 +176,8 @@ static const struct nlattr_parser nla_p_vlan[] = { NL_DECLARE_ATTR_PARSER(vlan_parser, nla_p_vlan); static int -create_vlan(struct nl_parsed_link *lattrs, struct nlpcb *nlp, struct nl_pstate *npt) +create_vlan(struct nl_parsed_link *lattrs, const struct nlattr_bmask *bm, + struct nlpcb *nlp, struct nl_pstate *npt) { struct epoch_tracker et; struct ifnet *ifp; @@ -147,9 +237,17 @@ create_vlan(struct nl_parsed_link *lattrs, struct nlpcb *nlp, struct nl_pstate * return (error); } +static int +dump_vlan(struct ifnet *ifp, struct nl_writer *nw) +{ + return (0); +} + static struct nl_cloner vlan_cloner = { .name = "vlan", .create_f = create_vlan, + .modify_f = modify_generic, + .dump_f = dump_vlan, }; diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h index 1b8f1cf7b53d..12a8aa718993 100644 --- a/sys/netlink/route/interface.h +++ b/sys/netlink/route/interface.h @@ -92,7 +92,7 @@ enum { #define IFLA_LINKINFO IFLA_LINKINFO IFLA_NET_NS_PID = 19, /* u32: vnet id (not supported) */ #define IFLA_NET_NS_PID IFLA_NET_NS_PID - IFLA_IFALIAS = 20, /* not supported */ + IFLA_IFALIAS = 20, /* string: interface description */ #define IFLA_IFALIAS IFLA_IFALIAS IFLA_NUM_VF = 21, /* not supported */ #define IFLA_NUM_VF IFLA_NUM_VF diff --git a/sys/netlink/route/route_var.h b/sys/netlink/route/route_var.h index 0bcfcc962020..f1e522c7ae05 100644 --- a/sys/netlink/route/route_var.h +++ b/sys/netlink/route/route_var.h @@ -66,24 +66,31 @@ struct nl_parsed_link { char *ifla_group; char *ifla_ifname; char *ifla_cloner; + char *ifla_ifalias; struct nlattr *ifla_idata; unsigned short ifi_type; int ifi_index; uint32_t ifla_mtu; + uint32_t ifi_flags; + uint32_t ifi_change; }; -typedef int rtnl_iface_create_f(struct nl_parsed_link *lattrs, struct nlpcb *nlp, - struct nl_pstate *npt); -typedef int rtnl_iface_modify_f(struct nl_parsed_link *lattrs, struct nlpcb *nlp, - struct nl_pstate *npt); +typedef int rtnl_iface_create_f(struct nl_parsed_link *lattrs, + const struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt); +typedef int rtnl_iface_modify_f(struct ifnet *ifp, struct nl_parsed_link *lattrs, + const struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt); +typedef int rtnl_iface_dump_f(struct ifnet *ifp, struct nl_writer *nw); struct nl_cloner { const char *name; rtnl_iface_create_f *create_f; rtnl_iface_modify_f *modify_f; + rtnl_iface_dump_f *dump_f; SLIST_ENTRY(nl_cloner) next; }; +extern struct nl_cloner generic_cloner; + void rtnl_ifaces_init(void); void rtnl_ifaces_destroy(void); void rtnl_iface_add_cloner(struct nl_cloner *cloner); diff --git a/tests/atf_python/sys/net/Makefile b/tests/atf_python/sys/net/Makefile index 05b1d8afe863..63efefd27142 100644 --- a/tests/atf_python/sys/net/Makefile +++ b/tests/atf_python/sys/net/Makefile @@ -2,7 +2,7 @@ .PATH: ${.CURDIR} -FILES= __init__.py rtsock.py tools.py vnet.py +FILES= __init__.py netlink.py rtsock.py tools.py vnet.py .include FILESDIR= ${TESTSBASE}/atf_python/sys/net diff --git a/tests/atf_python/sys/net/netlink.py b/tests/atf_python/sys/net/netlink.py new file mode 100644 index 000000000000..046519ce0343 --- /dev/null +++ b/tests/atf_python/sys/net/netlink.py @@ -0,0 +1,1495 @@ +#!/usr/local/bin/python3 +import os +import socket +import struct +import sys +import unittest +from ctypes import c_int +from ctypes import c_ubyte +from ctypes import c_uint +from ctypes import c_ushort +from ctypes import sizeof +from ctypes import Structure +from enum import auto +from enum import Enum +from typing import Any +from typing import Dict +from typing import List +from typing import NamedTuple + + +def roundup2(val: int, num: int) -> int: + if val % num: + return (val | (num - 1)) + 1 + else: + return val + + +def align4(val: int) -> int: + return roundup2(val, 4) + + +class SockaddrNl(Structure): + _fields_ = [ + ("nl_len", c_ubyte), + ("nl_family", c_ubyte), + ("nl_pad", c_ushort), + ("nl_pid", c_uint), + ("nl_groups", c_uint), + ] + + +class Nlmsghdr(Structure): + _fields_ = [ + ("nlmsg_len", c_uint), + ("nlmsg_type", c_ushort), + ("nlmsg_flags", c_ushort), + ("nlmsg_seq", c_uint), + ("nlmsg_pid", c_uint), + ] + + +class Nlmsgerr(Structure): + _fields_ = [ + ("error", c_int), + ("msg", Nlmsghdr), + ] + + +class NlErrattrType(Enum): + NLMSGERR_ATTR_UNUSED = 0 + NLMSGERR_ATTR_MSG = auto() + NLMSGERR_ATTR_OFFS = auto() + NLMSGERR_ATTR_COOKIE = auto() + NLMSGERR_ATTR_POLICY = auto() + + +class RtattrType(Enum): + RTA_UNSPEC = 0 + RTA_DST = auto() + RTA_SRC = auto() + RTA_IIF = auto() + RTA_OIF = auto() + RTA_GATEWAY = auto() + RTA_PRIORITY = auto() + RTA_PREFSRC = auto() + RTA_METRICS = auto() + RTA_MULTIPATH = auto() + RTA_PROTOINFO = auto() + RTA_FLOW = auto() + RTA_CACHEINFO = auto() + RTA_SESSION = auto() + RTA_MP_ALGO = auto() + RTA_TABLE = auto() + RTA_MARK = auto() + RTA_MFC_STATS = auto() + RTA_VIA = auto() + RTA_NEWDST = auto() + RTA_PREF = auto() + RTA_ENCAP_TYPE = auto() + RTA_ENCAP = auto() + RTA_EXPIRES = auto() + RTA_PAD = auto() + RTA_UID = auto() + RTA_TTL_PROPAGATE = auto() + RTA_IP_PROTO = auto() + RTA_SPORT = auto() + RTA_DPORT = auto() + RTA_NH_ID = auto() + + +class NlMsgType(Enum): + NLMSG_NOOP = 1 + NLMSG_ERROR = 2 + NLMSG_DONE = 3 + NLMSG_OVERRUN = 4 + + +class NlRtMsgType(Enum): + RTM_NEWLINK = 16 + RTM_DELLINK = 17 + RTM_GETLINK = 18 + RTM_SETLINK = 19 + RTM_NEWADDR = 20 + RTM_DELADDR = 21 + RTM_GETADDR = 22 + RTM_NEWROUTE = 24 + RTM_DELROUTE = 25 + RTM_GETROUTE = 26 + RTM_NEWNEIGH = 28 + RTM_DELNEIGH = 27 + RTM_GETNEIGH = 28 + RTM_NEWRULE = 32 + RTM_DELRULE = 33 + RTM_GETRULE = 34 + RTM_NEWQDISC = 36 + RTM_DELQDISC = 37 + RTM_GETQDISC = 38 + RTM_NEWTCLASS = 40 + RTM_DELTCLASS = 41 + RTM_GETTCLASS = 42 + RTM_NEWTFILTER = 44 + RTM_DELTFILTER = 45 + RTM_GETTFILTER = 46 + RTM_NEWACTION = 48 + RTM_DELACTION = 49 + RTM_GETACTION = 50 + RTM_NEWPREFIX = 52 + RTM_GETMULTICAST = 58 + RTM_GETANYCAST = 62 + RTM_NEWNEIGHTBL = 64 + RTM_GETNEIGHTBL = 66 + RTM_SETNEIGHTBL = 67 + RTM_NEWNDUSEROPT = 68 + RTM_NEWADDRLABEL = 72 + RTM_DELADDRLABEL = 73 + RTM_GETADDRLABEL = 74 + RTM_GETDCB = 78 + RTM_SETDCB = 79 + RTM_NEWNETCONF = 80 + RTM_GETNETCONF = 82 + RTM_NEWMDB = 84 + RTM_DELMDB = 85 + RTM_GETMDB = 86 + RTM_NEWNSID = 88 + RTM_DELNSID = 89 + RTM_GETNSID = 90 + RTM_NEWSTATS = 92 + RTM_GETSTATS = 94 + + +class RtAttr(Structure): + _fields_ = [ + ("rta_len", c_ushort), + ("rta_type", c_ushort), + ] + + +class RtMsgHdr(Structure): + _fields_ = [ + ("rtm_family", c_ubyte), + ("rtm_dst_len", c_ubyte), + ("rtm_src_len", c_ubyte), + ("rtm_tos", c_ubyte), + ("rtm_table", c_ubyte), + ("rtm_protocol", c_ubyte), + ("rtm_scope", c_ubyte), + ("rtm_type", c_ubyte), + ("rtm_flags", c_uint), + ] + + +class RtMsgFlags(Enum): + RTM_F_NOTIFY = 0x100 + RTM_F_CLONED = 0x200 + RTM_F_EQUALIZE = 0x400 + RTM_F_PREFIX = 0x800 + RTM_F_LOOKUP_TABLE = 0x1000 + RTM_F_FIB_MATCH = 0x2000 + RTM_F_OFFLOAD = 0x4000 + RTM_F_TRAP = 0x8000 + RTM_F_OFFLOAD_FAILED = 0x20000000 + + +class AddressFamilyLinux(Enum): + AF_INET = socket.AF_INET + AF_INET6 = socket.AF_INET6 + AF_NETLINK = 16 + + +class AddressFamilyBsd(Enum): + AF_INET = socket.AF_INET + AF_INET6 = socket.AF_INET6 + AF_NETLINK = 38 + + +class NlmBaseFlags(Enum): + NLM_F_REQUEST = 0x01 + NLM_F_MULTI = 0x02 + NLM_F_ACK = 0x04 + NLM_F_ECHO = 0x08 + NLM_F_DUMP_INTR = 0x10 + NLM_F_DUMP_FILTERED = 0x20 + + +# XXX: in python3.8 it is possible to +# class NlmGetFlags(Enum, NlmBaseFlags): + + +class NlmGetFlags(Enum): + NLM_F_ROOT = 0x100 + NLM_F_MATCH = 0x200 + NLM_F_ATOMIC = 0x400 + + +class NlmNewFlags(Enum): + NLM_F_REPLACE = 0x100 + NLM_F_EXCL = 0x200 + NLM_F_CREATE = 0x400 + NLM_F_APPEND = 0x800 + + +class NlmDeleteFlags(Enum): + NLM_F_NONREC = 0x100 + + +class NlmAckFlags(Enum): + NLM_F_CAPPED = 0x100 + NLM_F_ACK_TLVS = 0x200 + + +class RtScope(Enum): + RT_SCOPE_UNIVERSE = 0 + RT_SCOPE_SITE = 200 + RT_SCOPE_LINK = 253 + RT_SCOPE_HOST = 254 + RT_SCOPE_NOWHERE = 255 + + +class RtType(Enum): + RTN_UNSPEC = 0 + RTN_UNICAST = auto() + RTN_LOCAL = auto() + RTN_BROADCAST = auto() + RTN_ANYCAST = auto() + RTN_MULTICAST = auto() + RTN_BLACKHOLE = auto() + RTN_UNREACHABLE = auto() + RTN_PROHIBIT = auto() + RTN_THROW = auto() + RTN_NAT = auto() + RTN_XRESOLVE = auto() + + +class RtProto(Enum): + RTPROT_UNSPEC = 0 + RTPROT_REDIRECT = 1 + RTPROT_KERNEL = 2 + RTPROT_BOOT = 3 + RTPROT_STATIC = 4 + RTPROT_GATED = 8 + RTPROT_RA = 9 + RTPROT_MRT = 10 + RTPROT_ZEBRA = 11 + RTPROT_BIRD = 12 + RTPROT_DNROUTED = 13 + RTPROT_XORP = 14 + RTPROT_NTK = 15 + RTPROT_DHCP = 16 + RTPROT_MROUTED = 17 + RTPROT_KEEPALIVED = 18 + RTPROT_BABEL = 42 + RTPROT_OPENR = 99 + RTPROT_BGP = 186 + RTPROT_ISIS = 187 + RTPROT_OSPF = 188 + RTPROT_RIP = 189 + RTPROT_EIGRP = 192 + + +class NlRtaxType(Enum): + RTAX_UNSPEC = 0 + RTAX_LOCK = auto() + RTAX_MTU = auto() + RTAX_WINDOW = auto() + RTAX_RTT = auto() + RTAX_RTTVAR = auto() + RTAX_SSTHRESH = auto() + RTAX_CWND = auto() + RTAX_ADVMSS = auto() + RTAX_REORDERING = auto() + RTAX_HOPLIMIT = auto() + RTAX_INITCWND = auto() + RTAX_FEATURES = auto() + RTAX_RTO_MIN = auto() + RTAX_INITRWND = auto() + RTAX_QUICKACK = auto() + RTAX_CC_ALGO = auto() + RTAX_FASTOPEN_NO_COOKIE = auto() + + +class NlRtGroup(Enum): + RTNLGRP_NONE = 0 + RTNLGRP_LINK = auto() + RTNLGRP_NOTIFY = auto() + RTNLGRP_NEIGH = auto() + RTNLGRP_TC = auto() + RTNLGRP_IPV4_IFADDR = auto() + RTNLGRP_IPV4_MROUTE = auto() + RTNLGRP_IPV4_ROUTE = auto() + RTNLGRP_IPV4_RULE = auto() + RTNLGRP_IPV6_IFADDR = auto() + RTNLGRP_IPV6_MROUTE = auto() + RTNLGRP_IPV6_ROUTE = auto() + RTNLGRP_IPV6_IFINFO = auto() + RTNLGRP_DECnet_IFADDR = auto() + RTNLGRP_NOP2 = auto() + RTNLGRP_DECnet_ROUTE = auto() + RTNLGRP_DECnet_RULE = auto() + RTNLGRP_NOP4 = auto() + RTNLGRP_IPV6_PREFIX = auto() + RTNLGRP_IPV6_RULE = auto() + RTNLGRP_ND_USEROPT = auto() *** 1470 LINES SKIPPED ***