Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Apr 2023 12:37:20 GMT
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 089104e0e01f - main - netlink: add netlink interfaces to if_clone
Message-ID:  <202304251237.33PCbKJK044790@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=089104e0e01f080c9cd45dc5f34c4f636dea4ca7

commit 089104e0e01f080c9cd45dc5f34c4f636dea4ca7
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-04-19 12:35:02 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-04-25 12:34:46 +0000

    netlink: add netlink interfaces to if_clone
    
    This change adds netlink create/modify/dump interfaces to the `if_clone.c`.
    The previous attempt with storing the logic inside `netlink/route/iface_drivers.c`
     did not quite work, as, for example, dumping interface-specific state
     (like vlan id or vlan parent) required some peeking into the private interfaces.
    
    The new interfaces are added in a compatible way - callers don't have to do anything
    unless they are extended with Netlink.
    
    Reviewed by:    kp
    Differential Revision: https://reviews.freebsd.org/D39032
    MFC after:      1 month
---
 sys/net/if_clone.c                   | 163 +++++++++++++++++++++++++------
 sys/net/if_clone.h                   |  42 +++++++-
 sys/net/if_vlan.c                    | 180 ++++++++++++++++++++++++++++++++++-
 sys/netlink/netlink_glue.c           |  29 ++++++
 sys/netlink/netlink_module.c         |   1 +
 sys/netlink/netlink_var.h            |  10 ++
 sys/netlink/route/iface.c            |  65 ++++---------
 sys/netlink/route/iface_drivers.c    | 172 ++-------------------------------
 sys/netlink/route/route_var.h        |   6 ++
 tests/sys/netlink/test_rtnl_iface.py |   3 +
 10 files changed, 427 insertions(+), 244 deletions(-)

diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c
index 59d60645cb89..3dd577850f82 100644
--- a/sys/net/if_clone.c
+++ b/sys/net/if_clone.c
@@ -33,6 +33,8 @@
  * $FreeBSD$
  */
 
+#include "opt_netlink.h"
+
 #include <sys/param.h>
 #include <sys/eventhandler.h>
 #include <sys/malloc.h>
@@ -52,6 +54,11 @@
 #include <net/route.h>
 #include <net/vnet.h>
 
+#include <netlink/netlink.h>
+#include <netlink/netlink_ctl.h>
+#include <netlink/netlink_route.h>
+#include <netlink/route/route_var.h>
+
 /* Current IF_MAXUNIT expands maximum to 5 characters. */
 #define	IFCLOSIZ	(IFNAMSIZ - 5)
 
@@ -77,6 +84,10 @@ struct if_clone {
 	ifc_create_f *ifc_create;	/* (c) Creates new interface */
 	ifc_destroy_f *ifc_destroy;	/* (c) Destroys cloned interface */
 
+	ifc_create_nl_f	*create_nl;	/* (c) Netlink creation handler */
+	ifc_modify_nl_f	*modify_nl;	/* (c) Netlink modification handler */
+	ifc_dump_nl_f	*dump_nl;	/* (c) Netlink dump handler */
+
 #ifdef CLONE_COMPAT_13
 	/* (c) Driver specific cloning functions.  Called with no locks held. */
 	union {
@@ -104,8 +115,8 @@ struct if_clone {
 
 
 static void	if_clone_free(struct if_clone *ifc);
-static int	if_clone_createif(struct if_clone *ifc, char *name, size_t len,
-		    struct ifc_data *ifd, struct ifnet **ifpp);
+static int	if_clone_createif_nl(struct if_clone *ifc, const char *name,
+		    struct ifc_data_nl *ifd);
 
 static int ifc_simple_match(struct if_clone *ifc, const char *name);
 static int ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit);
@@ -188,27 +199,41 @@ vnet_if_clone_init(void)
  * Lookup and create a clone network interface.
  */
 int
-ifc_create_ifp(const char *name, struct ifc_data *ifd,
-    struct ifnet **ifpp)
+ifc_create_ifp(const char *name, struct ifc_data *ifd, struct ifnet **ifpp)
 {
-	struct if_clone *ifc;
-	char ifname[IFNAMSIZ];
-	struct ifnet *ifp = NULL;
-	int error;
+	struct if_clone *ifc = ifc_find_cloner_match(name);
 
-	/* Try to find an applicable cloner for this request */
-	ifc = ifc_find_cloner_match(name);
 	if (ifc == NULL)
 		return (EINVAL);
 
-	strlcpy(ifname, name, IFNAMSIZ);
-	error = if_clone_createif(ifc, ifname, IFNAMSIZ, ifd, &ifp);
+	struct ifc_data_nl ifd_new = {
+		.flags = ifd->flags,
+		.unit = ifd->unit,
+		.params = ifd->params,
+	};
+
+	int error = if_clone_createif_nl(ifc, name, &ifd_new);
+
 	if (ifpp != NULL)
-		*ifpp = ifp;
+		*ifpp = ifd_new.ifp;
 
 	return (error);
 }
 
+bool
+ifc_create_ifp_nl(const char *name, struct ifc_data_nl *ifd)
+{
+	struct if_clone *ifc = ifc_find_cloner_match(name);
+	if (ifc == NULL) {
+		ifd->error = EINVAL;
+		return (false);
+	}
+
+	ifd->error = if_clone_createif_nl(ifc, name, ifd);
+
+	return (true);
+}
+
 int
 if_clone_create(char *name, size_t len, caddr_t params)
 {
@@ -223,6 +248,62 @@ if_clone_create(char *name, size_t len, caddr_t params)
 	return (error);
 }
 
+bool
+ifc_modify_ifp_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
+{
+	struct if_clone *ifc = ifc_find_cloner(ifp->if_dname);
+	if (ifc == NULL) {
+		ifd->error = EINVAL;
+		return (false);
+	}
+
+	ifd->error = (*ifc->modify_nl)(ifp, ifd);
+	return (true);
+}
+
+bool
+ifc_dump_ifp_nl(struct ifnet *ifp, struct nl_writer *nw)
+{
+	struct if_clone *ifc = ifc_find_cloner(ifp->if_dname);
+	if (ifc == NULL)
+		return (false);
+
+	(*ifc->dump_nl)(ifp, nw);
+	return (true);
+}
+
+static int
+ifc_create_ifp_nl_default(struct if_clone *ifc, char *name, size_t len,
+    struct ifc_data_nl *ifd)
+{
+	struct ifc_data ifd_new = {
+		.flags = ifd->flags,
+		.unit = ifd->unit,
+		.params = ifd->params,
+	};
+
+	return ((*ifc->ifc_create)(ifc, name, len, &ifd_new, &ifd->ifp));
+}
+
+static int
+ifc_modify_ifp_nl_default(struct ifnet *ifp, struct ifc_data_nl *ifd)
+{
+	if (ifd->lattrs != NULL)
+		return (nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt));
+	return (0);
+}
+
+static void
+ifc_dump_ifp_nl_default(struct ifnet *ifp, struct nl_writer *nw)
+{
+	int off = nlattr_add_nested(nw, IFLA_LINKINFO);
+
+	if (off != 0) {
+		nlattr_add_string(nw, IFLA_INFO_KIND, ifp->if_dname);
+		nlattr_set_len(nw, off);
+	}
+}
+
 void
 ifc_link_ifp(struct if_clone *ifc, struct ifnet *ifp)
 {
@@ -306,29 +387,38 @@ ifc_find_cloner_in_vnet(const char *name, struct vnet *vnet)
  * Create a clone network interface.
  */
 static int
-if_clone_createif(struct if_clone *ifc, char *name, size_t len,
-    struct ifc_data *ifd, struct ifnet **ifpp)
+if_clone_createif_nl(struct if_clone *ifc, const char *ifname, struct ifc_data_nl *ifd)
 {
-	int err, unit = 0;
+	char name[IFNAMSIZ];
+	int error;
+
+	strlcpy(name, ifname, sizeof(name));
 
 	if (ifunit(name) != NULL)
 		return (EEXIST);
 
 	if (ifc->ifc_flags & IFC_F_AUTOUNIT) {
-		if ((err = ifc_handle_unit(ifc, name, len, &unit)) != 0)
-			return (err);
-		ifd->unit = unit;
+		if ((error = ifc_handle_unit(ifc, name, sizeof(name), &ifd->unit)) != 0)
+			return (error);
 	}
-	*ifpp = NULL;
-	err = (*ifc->ifc_create)(ifc, name, len, ifd, ifpp);
 
-	if (err == 0) {
-		MPASS(*ifpp != NULL);
-		if_clone_addif(ifc, *ifpp);
-	} else if (ifc->ifc_flags & IFC_F_AUTOUNIT)
-		ifc_free_unit(ifc, unit);
+	if (ifd->lattrs != NULL)
+		error = (*ifc->create_nl)(ifc, name, sizeof(name), ifd);
+	else
+		error = ifc_create_ifp_nl_default(ifc, name, sizeof(name), ifd);
+	if (error != 0) {
+		if (ifc->ifc_flags & IFC_F_AUTOUNIT)
+			ifc_free_unit(ifc, ifd->unit);
+		return (error);
+	}
 
-	return (err);
+	MPASS(ifd->ifp != NULL);
+	if_clone_addif(ifc, ifd->ifp);
+
+	if (ifd->lattrs != NULL)
+		error = (*ifc->modify_nl)(ifd->ifp, ifd);
+
+	return (error);
 }
 
 /*
@@ -408,6 +498,10 @@ if_clone_alloc(const char *name, int maxunit)
 	ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx);
 	LIST_INIT(&ifc->ifc_iflist);
 
+	ifc->create_nl = ifc_create_ifp_nl_default;
+	ifc->modify_nl = ifc_modify_ifp_nl_default;
+	ifc->dump_nl = ifc_dump_ifp_nl_default;
+
 	return (ifc);
 }
 
@@ -444,6 +538,16 @@ ifc_attach_cloner(const char *name, struct if_clone_addreq *req)
 	ifc->ifc_destroy = req->destroy_f;
 	ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP));
 
+	if (req->version == 2) {
+		struct if_clone_addreq_v2 *req2 = (struct if_clone_addreq_v2 *)req;
+
+		ifc->create_nl = req2->create_nl_f;
+		ifc->modify_nl = req2->modify_nl_f;
+		ifc->dump_nl = req2->dump_nl_f;
+	}
+
+	ifc->dump_nl = ifc_dump_ifp_nl_default;
+
 	if (if_clone_attach(ifc) != 0)
 		return (NULL);
 
@@ -546,11 +650,10 @@ if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy,
 	for (unit = 0; unit < minifs; unit++) {
 		char name[IFNAMSIZ];
 		int error __unused;
-		struct ifc_data ifd = {};
-		struct ifnet *ifp;
+		struct ifc_data_nl ifd = {};
 
 		snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit);
-		error = if_clone_createif(ifc, name, IFNAMSIZ, &ifd, &ifp);
+		error = if_clone_createif_nl(ifc, name, &ifd);
 		KASSERT(error == 0,
 		    ("%s: failed to create required interface %s",
 		    __func__, name));
diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h
index 1d918a012a5b..8b52c375addb 100644
--- a/sys/net/if_clone.h
+++ b/sys/net/if_clone.h
@@ -56,6 +56,26 @@ typedef int ifc_create_f(struct if_clone *ifc, char *name, size_t maxlen,
     struct ifc_data *ifd, struct ifnet **ifpp);
 typedef int ifc_destroy_f(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags);
 
+struct nl_parsed_link;
+struct nlattr_bmask;
+struct nl_pstate;
+struct nl_writer;
+struct ifc_data_nl {
+	struct nl_parsed_link		*lattrs;/* (in) Parsed link attributes */
+	const struct nlattr_bmask	*bm;	/* (in) Bitmask of set link attributes */
+	struct nl_pstate		*npt;	/* (in) Netlink context */
+	void				*params;/* (in) (Compat) data from ioctl */
+	uint32_t			flags;	/* (in) IFC_F flags */
+	uint32_t			unit;	/* (in/out) Selected unit when IFC_C_AUTOUNIT set */
+	int				error;	/* (out) Return error code */
+	struct ifnet			*ifp;	/* (out) Returned ifp */
+};
+
+typedef int ifc_create_nl_f(struct if_clone *ifc, char *name, size_t maxlen,
+    struct ifc_data_nl *ifd);
+typedef int ifc_modify_nl_f(struct ifnet *ifp, struct ifc_data_nl *ifd);
+typedef void ifc_dump_nl_f(struct ifnet *ifp, struct nl_writer *nw);
+
 struct if_clone_addreq {
 	uint16_t	version; /* Always 0 for now */
 	uint16_t	spare;
@@ -66,17 +86,35 @@ struct if_clone_addreq {
 	ifc_destroy_f	*destroy_f;
 };
 
+struct if_clone_addreq_v2 {
+	uint16_t	version; /* 2 */
+	uint16_t	spare;
+	uint32_t	flags;
+	uint32_t	maxunit; /* Maximum allowed unit number */
+	ifc_match_f	*match_f;
+	ifc_create_f	*create_f;
+	ifc_destroy_f	*destroy_f;
+	ifc_create_nl_f	*create_nl_f;
+	ifc_modify_nl_f	*modify_nl_f;
+	ifc_dump_nl_f	*dump_nl_f;
+};
+
+
 #define	IFC_F_NOGROUP	0x01	/* Creation flag: don't add unit group */
 #define	IFC_F_AUTOUNIT	0x02	/* Creation flag: automatically select unit */
 #define	IFC_F_SYSSPACE	0x04	/* Cloner callback: params pointer is in kernel memory */
 #define	IFC_F_FORCE	0x08	/* Deletion flag: force interface deletion */
+#define	IFC_F_CREATE	0x10	/* Creation flag: indicate creation request */
 
 #define	IFC_NOGROUP	IFC_F_NOGROUP
 
 struct if_clone	*ifc_attach_cloner(const char *name, struct if_clone_addreq *req);
 void ifc_detach_cloner(struct if_clone *ifc);
-int ifc_create_ifp(const char *name, struct ifc_data *ifd,
-    struct ifnet **ifpp);
+int ifc_create_ifp(const char *name, struct ifc_data *ifd, struct ifnet **ifpp);
+
+bool ifc_create_ifp_nl(const char *name, struct ifc_data_nl *ifd);
+bool ifc_modify_ifp_nl(struct ifnet *ifp, struct ifc_data_nl *ifd);
+bool ifc_dump_ifp_nl(struct ifnet *ifp, struct nl_writer *nw);
 
 void ifc_link_ifp(struct if_clone *ifc, struct ifnet *ifp);
 bool ifc_unlink_ifp(struct if_clone *ifc, struct ifnet *ifp);
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
index 0f2ded3f6040..f5b401c446ed 100644
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_inet.h"
 #include "opt_inet6.h"
 #include "opt_kern_tls.h"
+#include "opt_netlink.h"
 #include "opt_vlan.h"
 #include "opt_ratelimit.h"
 
@@ -85,6 +86,11 @@ __FBSDID("$FreeBSD$");
 #include <netinet/if_ether.h>
 #endif
 
+#include <netlink/netlink.h>
+#include <netlink/netlink_ctl.h>
+#include <netlink/netlink_route.h>
+#include <netlink/route/route_var.h>
+
 #define	VLAN_DEF_HWIDTH	4
 #define	VLAN_IFFLAGS	(IFF_BROADCAST | IFF_MULTICAST)
 
@@ -320,6 +326,11 @@ static	int vlan_clone_create(struct if_clone *, char *, size_t,
     struct ifc_data *, struct ifnet **);
 static	int vlan_clone_destroy(struct if_clone *, struct ifnet *, uint32_t);
 
+static int vlan_clone_create_nl(struct if_clone *ifc, char *name, size_t len,
+    struct ifc_data_nl *ifd);
+static int vlan_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd);
+static void vlan_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw);
+
 static	void vlan_ifdetach(void *arg, struct ifnet *ifp);
 static  void vlan_iflladdr(void *arg, struct ifnet *ifp);
 static  void vlan_ifevent(void *arg, struct ifnet *ifp, int event);
@@ -896,10 +907,14 @@ extern	void (*vlan_input_p)(struct ifnet *, struct mbuf *);
 /* For if_link_state_change() eyes only... */
 extern	void (*vlan_link_state_p)(struct ifnet *);
 
-static struct if_clone_addreq vlan_addreq = {
+static struct if_clone_addreq_v2 vlan_addreq = {
+	.version = 2,
 	.match_f = vlan_clone_match,
 	.create_f = vlan_clone_create,
 	.destroy_f = vlan_clone_destroy,
+	.create_nl_f = vlan_clone_create_nl,
+	.modify_nl_f = vlan_clone_modify_nl,
+	.dump_nl_f = vlan_clone_dump_nl,
 };
 
 static int
@@ -931,7 +946,7 @@ vlan_modevent(module_t mod, int type, void *data)
 		vlan_pcp_p = vlan_pcp;
 		vlan_devat_p = vlan_devat;
 #ifndef VIMAGE
-		vlan_cloner = ifc_attach_cloner(vlanname, &vlan_addreq);
+		vlan_cloner = ifc_attach_cloner(vlanname, (struct if_clone_addreq *)&vlan_addreq);
 #endif
 		if (bootverbose)
 			printf("vlan: initialized, using "
@@ -981,7 +996,7 @@ MODULE_VERSION(if_vlan, 3);
 static void
 vnet_vlan_init(const void *unused __unused)
 {
-	vlan_cloner = ifc_attach_cloner(vlanname, &vlan_addreq);
+	vlan_cloner = ifc_attach_cloner(vlanname, (struct if_clone_addreq *)&vlan_addreq);
 	V_vlan_cloner = vlan_cloner;
 }
 VNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
@@ -1222,6 +1237,165 @@ vlan_clone_create(struct if_clone *ifc, char *name, size_t len,
 	return (0);
 }
 
+/*
+ *
+ * Parsers of IFLA_INFO_DATA inside IFLA_LINKINFO of RTM_NEWLINK
+ *    {{nla_len=8, nla_type=IFLA_LINK}, 2},
+ *    {{nla_len=12, nla_type=IFLA_IFNAME}, "xvlan22"},
+ *    {{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\x16\x00\x00\x00"}]}
+ */
+
+struct nl_parsed_vlan {
+	uint16_t vlan_id;
+	uint16_t vlan_proto;
+	struct ifla_vlan_flags vlan_flags;
+};
+
+#define	_OUT(_field)	offsetof(struct nl_parsed_vlan, _field)
+static const struct nlattr_parser nla_p_vlan[] = {
+	{ .type = IFLA_VLAN_ID, .off = _OUT(vlan_id), .cb = nlattr_get_uint16 },
+	{ .type = IFLA_VLAN_FLAGS, .off = _OUT(vlan_flags), .cb = nlattr_get_nla },
+	{ .type = IFLA_VLAN_PROTOCOL, .off = _OUT(vlan_proto), .cb = nlattr_get_uint16 },
+};
+#undef _OUT
+NL_DECLARE_ATTR_PARSER(vlan_parser, nla_p_vlan);
+
+static int
+vlan_clone_create_nl(struct if_clone *ifc, char *name, size_t len,
+    struct ifc_data_nl *ifd)
+{
+	struct epoch_tracker et;
+        struct ifnet *ifp_parent;
+	struct nl_pstate *npt = ifd->npt;
+	struct nl_parsed_link *lattrs = ifd->lattrs;
+	int error;
+
+	/*
+	 * lattrs.ifla_ifname is the new interface name
+	 * lattrs.ifi_index contains parent interface index
+	 * lattrs.ifla_idata contains un-parsed vlan data
+	 */
+	struct nl_parsed_vlan attrs = {
+		.vlan_id = 0xFEFE,
+		.vlan_proto = ETHERTYPE_VLAN
+	};
+
+	if (lattrs->ifla_idata == NULL) {
+		nlmsg_report_err_msg(npt, "vlan id is required, guessing not supported");
+		return (ENOTSUP);
+	}
+
+	error = nl_parse_nested(lattrs->ifla_idata, &vlan_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+	if (attrs.vlan_id > 4095) {
+		nlmsg_report_err_msg(npt, "Invalid VID: %d", attrs.vlan_id);
+		return (EINVAL);
+	}
+	if (attrs.vlan_proto != ETHERTYPE_VLAN && attrs.vlan_proto != ETHERTYPE_QINQ) {
+		nlmsg_report_err_msg(npt, "Unsupported ethertype: 0x%04X", attrs.vlan_proto);
+		return (ENOTSUP);
+	}
+
+	struct vlanreq params = {
+		.vlr_tag = attrs.vlan_id,
+		.vlr_proto = attrs.vlan_proto,
+	};
+	struct ifc_data ifd_new = { .flags = IFC_F_SYSSPACE, .unit = ifd->unit, .params = &params };
+
+	NET_EPOCH_ENTER(et);
+	ifp_parent = ifnet_byindex(lattrs->ifi_index);
+	if (ifp_parent != NULL)
+		strlcpy(params.vlr_parent, if_name(ifp_parent), sizeof(params.vlr_parent));
+	NET_EPOCH_EXIT(et);
+
+	if (ifp_parent == NULL) {
+		nlmsg_report_err_msg(npt, "unable to find parent interface %u", lattrs->ifi_index);
+		return (ENOENT);
+	}
+
+	error = vlan_clone_create(ifc, name, len, &ifd_new, &ifd->ifp);
+
+	return (error);
+}
+
+static int
+vlan_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
+{
+	struct nl_parsed_link *lattrs = ifd->lattrs;
+
+	if ((lattrs->ifla_idata != NULL) && ((ifd->flags & IFC_F_CREATE) == 0)) {
+		struct epoch_tracker et;
+		struct nl_parsed_vlan attrs = {
+			.vlan_proto = ETHERTYPE_VLAN,
+		};
+		int error;
+
+		error = nl_parse_nested(lattrs->ifla_idata, &vlan_parser, ifd->npt, &attrs);
+		if (error != 0)
+			return (error);
+
+		NET_EPOCH_ENTER(et);
+		struct ifnet *ifp_parent = ifnet_byindex_ref(lattrs->ifla_link);
+		NET_EPOCH_EXIT(et);
+
+		if (ifp_parent == NULL) {
+			nlmsg_report_err_msg(ifd->npt, "unable to find parent interface %u",
+			    lattrs->ifla_link);
+			return (ENOENT);
+		}
+
+		struct ifvlan *ifv = ifp->if_softc;
+		error = vlan_config(ifv, ifp_parent, attrs.vlan_id, attrs.vlan_proto);
+
+		if_rele(ifp_parent);
+		if (error != 0)
+			return (error);
+	}
+
+	return (nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt));
+}
+
+/*
+ *    {{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\x16\x00\x00\x00"}]}
+ */
+static void
+vlan_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
+{
+	uint32_t parent_index = 0;
+	uint16_t vlan_id = 0;
+	uint16_t vlan_proto = 0;
+
+	VLAN_SLOCK();
+	struct ifvlan *ifv = ifp->if_softc;
+	if (TRUNK(ifv) != NULL)
+		parent_index = PARENT(ifv)->if_index;
+	vlan_id = ifv->ifv_vid;
+	vlan_proto = ifv->ifv_proto;
+	VLAN_SUNLOCK();
+
+	if (parent_index != 0)
+		nlattr_add_u32(nw, IFLA_LINK, parent_index);
+
+	int off = nlattr_add_nested(nw, IFLA_LINKINFO);
+	if (off != 0) {
+		nlattr_add_string(nw, IFLA_INFO_KIND, "vlan");
+		int off2 = nlattr_add_nested(nw, IFLA_INFO_DATA);
+		if (off2 != 0) {
+			nlattr_add_u16(nw, IFLA_VLAN_ID, vlan_id);
+			nlattr_add_u16(nw, IFLA_VLAN_PROTOCOL, vlan_proto);
+			nlattr_set_len(nw, off2);
+		}
+		nlattr_set_len(nw, off);
+	}
+}
+
 static int
 vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
 {
diff --git a/sys/netlink/netlink_glue.c b/sys/netlink/netlink_glue.c
index 25b891036b5b..069cb9900e03 100644
--- a/sys/netlink/netlink_glue.c
+++ b/sys/netlink/netlink_glue.c
@@ -177,6 +177,19 @@ nlmsg_end_dump_stub(struct nl_writer *nw, int error, struct nlmsghdr *hdr)
 	return (false);
 }
 
+static int
+nl_modify_ifp_generic_stub(struct ifnet *ifp __unused,
+    struct nl_parsed_link *lattrs __unused, const struct nlattr_bmask *bm __unused,
+    struct nl_pstate *npt __unused)
+{
+	return (ENOTSUP);
+}
+
+static void
+nl_store_ifp_cookie_stub(struct nl_pstate *npt __unused, struct ifnet *ifp __unused)
+{
+}
+
 const static struct nl_function_wrapper nl_stub = {
 	.nlmsg_add = nlmsg_add_stub,
 	.nlmsg_refill_buffer = nlmsg_refill_buffer_stub,
@@ -188,6 +201,8 @@ const static struct nl_function_wrapper nl_stub = {
 	.nlmsg_get_group_writer = nlmsg_get_group_writer_stub,
 	.nlmsg_get_chain_writer = nlmsg_get_chain_writer_stub,
 	.nlmsg_end_dump = nlmsg_end_dump_stub,
+	.nl_modify_ifp_generic = nl_modify_ifp_generic_stub,
+	.nl_store_ifp_cookie = nl_store_ifp_cookie_stub,
 };
 
 /*
@@ -262,5 +277,19 @@ nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr)
 {
 	return (_nl->nlmsg_end_dump(nw, error, hdr));
 }
+
+int
+nl_modify_ifp_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs,
+    const struct nlattr_bmask *bm , struct nl_pstate *npt)
+{
+	return (_nl->nl_modify_ifp(ifp, lattrs, bm, npt));
+}
+
+static void
+nl_store_ifp_cookie_stub(struct nl_pstate *npt, struct ifnet *ifp)
+{
+	return (_nl->nl_store_ifp_cookie(npt, ifp));
+}
+
 #endif /* !NETLINK */
 
diff --git a/sys/netlink/netlink_module.c b/sys/netlink/netlink_module.c
index a881a7540166..051eb0cb120b 100644
--- a/sys/netlink/netlink_module.c
+++ b/sys/netlink/netlink_module.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include <netlink/netlink.h>
 #include <netlink/netlink_ctl.h>
 #include <netlink/netlink_var.h>
+#include <netlink/route/route_var.h>
 
 #include <machine/atomic.h>
 
diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h
index 465378f8af1e..cb1e3974b5f5 100644
--- a/sys/netlink/netlink_var.h
+++ b/sys/netlink/netlink_var.h
@@ -172,6 +172,11 @@ struct genl_group *genl_get_group(uint32_t group_id);
 
 #define	CTRL_FAMILY_NAME	"nlctrl"
 
+struct ifnet;
+struct nl_parsed_link;
+struct nlattr_bmask;
+struct nl_pstate;
+
 /* Function map */
 struct nl_function_wrapper {
 	bool (*nlmsg_add)(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
@@ -185,8 +190,13 @@ struct nl_function_wrapper {
 	bool (*nlmsg_get_group_writer)(struct nl_writer *nw, int size, int protocol, int group_id);
 	bool (*nlmsg_get_chain_writer)(struct nl_writer *nw, int size, struct mbuf **pm);
 	bool (*nlmsg_end_dump)(struct nl_writer *nw, int error, struct nlmsghdr *hdr);
+	int (*nl_modify_ifp_generic)(struct ifnet *ifp, struct nl_parsed_link *lattrs,
+	    const struct nlattr_bmask *bm, struct nl_pstate *npt);
+	void (*nl_store_ifp_cookie)(struct nl_pstate *npt, struct ifnet *ifp);
 };
 void nl_set_functions(const struct nl_function_wrapper *nl);
 
+
+
 #endif
 #endif
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index b27a0193fe0d..d81dc1f0ecae 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -303,13 +303,7 @@ dump_iface(struct nl_writer *nw, struct ifnet *ifp, const struct nlmsghdr *hdr,
 	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);
+	ifc_dump_ifp_nl(ifp, nw);
 
         if (nlmsg_end(nw))
 		return (true);
@@ -353,7 +347,7 @@ NL_DECLARE_ATTR_PARSER(linfo_parser, nla_p_linfo);
 static const struct nlattr_parser nla_p_if[] = {
 	{ .type = IFLA_IFNAME, .off = _OUT(ifla_ifname), .cb = nlattr_get_string },
 	{ .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = nlattr_get_uint32 },
-	{ .type = IFLA_LINK, .off = _OUT(ifi_index), .cb = nlattr_get_uint32 },
+	{ .type = IFLA_LINK, .off = _OUT(ifla_link), .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 },
@@ -545,21 +539,16 @@ create_link(struct nlmsghdr *hdr, struct nl_parsed_link *lattrs,
 		return (EINVAL);
 	}
 
-	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);
+	struct ifc_data_nl ifd = {
+		.flags = IFC_F_CREATE,
+		.lattrs = lattrs,
+		.bm = bm,
+		.npt = npt,
+	};
+	if (ifc_create_ifp_nl(lattrs->ifla_ifname, &ifd) && ifd.error == 0)
+		nl_store_ifp_cookie(npt, ifd.ifp);
 
-	return (error);
+	return (ifd.error);
 }
 
 static int
@@ -602,31 +591,20 @@ modify_link(struct nlmsghdr *hdr, struct nl_parsed_link *lattrs,
 	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.
+	 * Modification request can address either
+	 * 1) cloned interface, in which case we call the cloner-specific
+	 *  modification routine
+	 * or
+	 * 2) non-cloned (e.g. "physical") interface, in which case we call
+	 *  generic modification routine
 	 */
-	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);
-
-	if (!found)
-		error = generic_cloner.modify_f(ifp, lattrs, bm, nlp, npt);
+	struct ifc_data_nl ifd = { .lattrs = lattrs, .bm = bm, .npt = npt };
+	if (!ifc_modify_ifp_nl(ifp, &ifd))
+		ifd.error = nl_modify_ifp_generic(ifp, lattrs, bm, npt);
 
 	if_rele(ifp);
 
-	return (error);
+	return (ifd.error);
 }
 
 
@@ -1067,7 +1045,6 @@ rtnl_ifaces_init(void)
 	    ifnet_link_event, rtnl_handle_iflink, NULL,
 	    EVENTHANDLER_PRI_ANY);
 	NL_VERIFY_PARSERS(all_parsers);
-	rtnl_iface_drivers_register();
 	rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers));
 }
 
diff --git a/sys/netlink/route/iface_drivers.c b/sys/netlink/route/iface_drivers.c
index be28a0f3b676..17fbc1000d23 100644
--- a/sys/netlink/route/iface_drivers.c
+++ b/sys/netlink/route/iface_drivers.c
@@ -63,14 +63,14 @@ _DECLARE_DEBUG(LOG_DEBUG);
  * 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
+nl_modify_ifp_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs,
+    const struct nlattr_bmask *bm, struct nl_pstate *npt)
 {
 	int error;
 
 	if (lattrs->ifla_ifalias != NULL) {
-		if (nlp_has_priv(nlp, PRIV_NET_SETIFDESCR)) {
+		if (nlp_has_priv(npt->nlp, PRIV_NET_SETIFDESCR)) {
 			int len = strlen(lattrs->ifla_ifalias) + 1;
 			char *buf = if_allocdescr(len, M_WAITOK);
 
@@ -89,7 +89,7 @@ modify_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs,
 	}
 
 	if (lattrs->ifla_mtu > 0) {
-		if (nlp_has_priv(nlp, PRIV_NET_SETIFMTU)) {
+		if (nlp_has_priv(npt->nlp, PRIV_NET_SETIFMTU)) {
 			struct ifreq ifr = { .ifr_mtu = lattrs->ifla_mtu };
 			error = ifhwioctl(SIOCSIFMTU, ifp, (char *)&ifr, curthread);
 		} else {
@@ -117,8 +117,8 @@ modify_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs,
  *  IFLA_NEW_IFINDEX(u32)
  *  IFLA_IFNAME(string)
  */
-static void
-store_cookie(struct nl_pstate *npt, struct ifnet *ifp)
+void
+nl_store_ifp_cookie(struct nl_pstate *npt, struct ifnet *ifp)
 {
 	int ifname_len = strlen(if_name(ifp));
 	uint32_t ifindex = (uint32_t)ifp->if_index;
@@ -144,161 +144,3 @@ store_cookie(struct nl_pstate *npt, struct ifnet *ifp)
 	nlmsg_report_cookie(npt, nla_cookie);
 }
 
-static int
-create_generic_ifd(struct nl_parsed_link *lattrs, const struct nlattr_bmask *bm,
-    struct ifc_data *ifd, struct nlpcb *nlp, struct nl_pstate *npt)
-{
-	int error = 0;
-
-	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 (error == 0)
-			store_cookie(npt, ifp);
-		if_rele(ifp);
-	}
-
-	return (error);
-}
-/*
- * 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)
-{
-	struct ifc_data ifd = {};
-
-	return (create_generic_ifd(lattrs, bm, &ifd, nlp, npt));
-}
-
-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},
- *  {ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0},
- *   [
- *    {{nla_len=8, nla_type=IFLA_LINK}, 2},
- *    {{nla_len=12, nla_type=IFLA_IFNAME}, "xvlan22"},
- *    {{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\x16\x00\x00\x00"}]}]}, iov_len=76}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 76
- */
-
-struct nl_parsed_vlan {
-	uint16_t vlan_id;
-	uint16_t vlan_proto;
-	struct ifla_vlan_flags vlan_flags;
-};
-
-#define	_OUT(_field)	offsetof(struct nl_parsed_vlan, _field)
-static const struct nlattr_parser nla_p_vlan[] = {
-	{ .type = IFLA_VLAN_ID, .off = _OUT(vlan_id), .cb = nlattr_get_uint16 },
-	{ .type = IFLA_VLAN_FLAGS, .off = _OUT(vlan_flags), .cb = nlattr_get_nla },
-	{ .type = IFLA_VLAN_PROTOCOL, .off = _OUT(vlan_proto), .cb = nlattr_get_uint16 },
-};
-#undef _OUT
-NL_DECLARE_ATTR_PARSER(vlan_parser, nla_p_vlan);
-
-static int
-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;
-	int error;
-
-	/*
-	 * lattrs.ifla_ifname is the new interface name
-	 * lattrs.ifi_index contains parent interface index
-	 * lattrs.ifla_idata contains un-parsed vlan data
-	 */
-
-	struct nl_parsed_vlan attrs = {
-		.vlan_id = 0xFEFE,
-		.vlan_proto = ETHERTYPE_VLAN
-	};
-	NLP_LOG(LOG_DEBUG3, nlp, "nested: %p len %d", lattrs->ifla_idata, lattrs->ifla_idata->nla_len);
-
-	if (lattrs->ifla_idata == NULL) {
-		NLMSG_REPORT_ERR_MSG(npt, "vlan id is required, guessing not supported");
-		return (ENOTSUP);
-	}
-
-	error = nl_parse_nested(lattrs->ifla_idata, &vlan_parser, npt, &attrs);
-	if (error != 0)
-		return (error);
-	if (attrs.vlan_id > 4095) {
-		NLMSG_REPORT_ERR_MSG(npt, "Invalid VID: %d", attrs.vlan_id);
-		return (EINVAL);
-	}
-	if (attrs.vlan_proto != ETHERTYPE_VLAN && attrs.vlan_proto != ETHERTYPE_QINQ) {
-		NLMSG_REPORT_ERR_MSG(npt, "Unsupported ethertype: 0x%04X", attrs.vlan_proto);
-		return (ENOTSUP);
-	}
-
-	NET_EPOCH_ENTER(et);
-	ifp = ifnet_byindex_ref(lattrs->ifi_index);
-	NET_EPOCH_EXIT(et);
-	if (ifp == NULL) {
-		NLP_LOG(LOG_DEBUG, nlp, "unable to find parent interface %u",
-		    lattrs->ifi_index);
-		return (ENOENT);
-	}
-
-	struct vlanreq params = {
-		.vlr_tag = attrs.vlan_id,
-		.vlr_proto = attrs.vlan_proto,
-	};
-	strlcpy(params.vlr_parent, if_name(ifp), sizeof(params.vlr_parent));
-	struct ifc_data ifd = { .flags = IFC_F_SYSSPACE, .params = &params };
-
-	error = create_generic_ifd(lattrs, bm, &ifd, nlp, npt);
-
-	if_rele(ifp);
-	return (error);
-}
-
-static int
-dump_vlan(struct ifnet *ifp, struct nl_writer *nw)
-{
*** 70 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202304251237.33PCbKJK044790>