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
This commit is contained in:
Alexander V. Chernikov 2023-04-19 12:35:02 +00:00
parent 91fbe0819b
commit 089104e0e0
10 changed files with 427 additions and 244 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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)
{

View File

@ -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 */

View File

@ -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>

View File

@ -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

View File

@ -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;
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);
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);
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));
}

View File

@ -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)
{
return (0);
}
static struct nl_cloner vlan_cloner = {
.name = "vlan",
.create_f = create_vlan,
.modify_f = modify_generic,
.dump_f = dump_vlan,
};
static const struct nlhdr_parser *all_parsers[] = { &vlan_parser };
void
rtnl_iface_drivers_register(void)
{
rtnl_iface_add_cloner(&vlan_cloner);
NL_VERIFY_PARSERS(all_parsers);
}

View File

@ -71,11 +71,17 @@ struct nl_parsed_link {
struct nlattr *ifla_idata;
unsigned short ifi_type;
int ifi_index;
uint32_t ifla_link;
uint32_t ifla_mtu;
uint32_t ifi_flags;
uint32_t ifi_change;
};
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);
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,

View File

@ -19,6 +19,7 @@
from atf_python.sys.netlink.netlink_route import NlRtMsgType
from atf_python.sys.netlink.netlink_route import rtnl_ifla_attrs
from atf_python.sys.net.vnet import SingleVnetTestTemplate
from atf_python.sys.net.tools import ToolsHelper
class TestRtNlIface(NetlinkTestTemplate, SingleVnetTestTemplate):
@ -324,6 +325,7 @@ def test_create_vlan_plain(self):
msg.nl_hdr.nlmsg_flags = (
flags | NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
)
msg.base_hdr.ifi_index = ifindex
msg.add_nla(NlAttrU32(IflattrType.IFLA_LINK, ifindex))
msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, "vlan22"))
@ -347,5 +349,6 @@ def test_create_vlan_plain(self):
assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
assert rx_msg.error_code == 0
ToolsHelper.print_net_debug()
self.get_interface_byname("vlan22")
# ToolsHelper.print_net_debug()