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:
parent
91fbe0819b
commit
089104e0e0
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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 = ¶ms };
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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 = ¶ms };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user