diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h --- a/sys/net/if_clone.h +++ b/sys/net/if_clone.h @@ -56,6 +56,25 @@ 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 { + uint32_t flags; /* (in) IFC_F flags */ + 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 */ + uint32_t unit; /* (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 +85,35 @@ 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_clone.c b/sys/net/if_clone.c --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -52,6 +52,11 @@ #include #include +#include +#include +#include +#include + /* Current IF_MAXUNIT expands maximum to 5 characters. */ #define IFCLOSIZ (IFNAMSIZ - 5) @@ -77,6 +82,10 @@ 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,11 +113,15 @@ static void if_clone_free(struct if_clone *ifc); -static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, +static int if_clone_createif(struct if_clone *ifc, const char *name, 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, struct ifnet **ifpp); 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); +static struct if_clone *ifc_find_cloner(const char *name); +static struct if_clone *ifc_find_cloner_match(const char *name); #ifdef CLONE_COMPAT_13 static int ifc_simple_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, @@ -186,33 +199,36 @@ * 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; - - /* Try to find an applicable cloner for this request */ - IF_CLONERS_LOCK(); - LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { - if (ifc->ifc_match(ifc, name)) - break; - } - IF_CLONERS_UNLOCK(); + struct if_clone *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 ifnet *ifp = NULL; + int error = if_clone_createif(ifc, name, ifd, &ifp); + if (ifpp != NULL) *ifpp = 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, &ifd->ifp); + + return (true); +} + int if_clone_create(char *name, size_t len, caddr_t params) { @@ -227,6 +243,59 @@ 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 }; + printf("Request to create %s len %d unit %d\n", name, (int)len, ifd->unit); + + 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) { @@ -266,11 +335,25 @@ } static struct if_clone * -ifc_find_cloner(const char *name, struct vnet *vnet) +ifc_find_cloner_match(const char *name) +{ + struct if_clone *ifc; + + IF_CLONERS_LOCK(); + LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { + if (ifc->ifc_match(ifc, name)) + break; + } + IF_CLONERS_UNLOCK(); + + return (ifc); +} + +static struct if_clone * +ifc_find_cloner(const char *name) { struct if_clone *ifc; - CURVNET_SET_QUIET(vnet); IF_CLONERS_LOCK(); LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { if (strcmp(ifc->ifc_name, name) == 0) { @@ -278,6 +361,15 @@ } } IF_CLONERS_UNLOCK(); + + return (ifc); +} + +static struct if_clone * +ifc_find_cloner_vnet(const char *name, struct vnet *vnet) +{ + CURVNET_SET_QUIET(vnet); + struct if_clone *ifc = ifc_find_cloner(name); CURVNET_RESTORE(); return (ifc); @@ -287,29 +379,66 @@ * 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(struct if_clone *ifc, const char *ifname, struct ifc_data *ifd, + struct ifnet **ifpp) { - 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); + error = (*ifc->ifc_create)(ifc, name, sizeof(name), ifd, ifpp); + if (error != 0) { + if (ifc->ifc_flags & IFC_F_AUTOUNIT) + ifc_free_unit(ifc, ifd->unit); + return (error); + } - if (err == 0) { - MPASS(*ifpp != NULL); - if_clone_addif(ifc, *ifpp); - } else if (ifc->ifc_flags & IFC_F_AUTOUNIT) - ifc_free_unit(ifc, unit); + MPASS(*ifpp != NULL); + if_clone_addif(ifc, *ifpp); - return (err); + return (error); +} + +static int +if_clone_createif_nl(struct if_clone *ifc, const char *ifname, struct ifc_data_nl *ifd, + struct ifnet **ifpp) +{ + char name[IFNAMSIZ]; + int error; + + strlcpy(name, ifname, sizeof(name)); + + if (ifunit(name) != NULL) + return (EEXIST); + + if (ifc->ifc_flags & IFC_F_AUTOUNIT) { + if ((error = ifc_handle_unit(ifc, name, sizeof(name), &ifd->unit)) != 0) + return (error); + } + *ifpp = NULL; + error = (*ifc->create_nl)(ifc, name, sizeof(name), ifd); + if (error != 0) { + if (ifc->ifc_flags & IFC_F_AUTOUNIT) + ifc_free_unit(ifc, ifd->unit); + return (error); + } + *ifpp = ifd->ifp; + + MPASS(*ifpp != NULL); + if_clone_addif(ifc, *ifpp); + + error = (*ifc->modify_nl)(*ifpp, ifd); + + return (error); } /* @@ -326,7 +455,7 @@ if (ifp == NULL) return (ENXIO); - ifc = ifc_find_cloner(ifp->if_dname, ifp->if_home_vnet); + ifc = ifc_find_cloner_vnet(ifp->if_dname, ifp->if_home_vnet); if (ifc == NULL) { if_rele(ifp); return (EINVAL); @@ -425,6 +554,21 @@ 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; + } + + if (ifc->create_nl == NULL) + ifc->create_nl = ifc_create_ifp_nl_default; + if (ifc->modify_nl == NULL) + ifc->modify_nl = ifc_modify_ifp_nl_default; + if (ifc->dump_nl == NULL) + ifc->dump_nl = ifc_dump_ifp_nl_default; + if (if_clone_attach(ifc) != 0) return (NULL); @@ -531,7 +675,7 @@ struct ifnet *ifp; snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); - error = if_clone_createif(ifc, name, IFNAMSIZ, &ifd, &ifp); + error = if_clone_createif(ifc, name, &ifd, &ifp); KASSERT(error == 0, ("%s: failed to create required interface %s", __func__, name)); diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -85,6 +85,11 @@ #include #endif +#include +#include +#include +#include + #define VLAN_DEF_HWIDTH 4 #define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST) @@ -320,6 +325,11 @@ 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 +906,14 @@ /* 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 +945,7 @@ 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 " @@ -976,12 +990,13 @@ DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(if_vlan, 3); +MODULE_DEPEND(linux_common, netlink, 1, 1, 1); #ifdef VIMAGE 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 @@ 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) { diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c --- a/sys/netlink/route/iface.c +++ b/sys/netlink/route/iface.c @@ -299,13 +299,7 @@ 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); @@ -349,7 +343,7 @@ 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 }, @@ -541,21 +535,16 @@ 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 @@ -598,31 +587,20 @@ 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); } @@ -1057,7 +1035,6 @@ 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 --- a/sys/netlink/route/iface_drivers.c +++ b/sys/netlink/route/iface_drivers.c @@ -63,14 +63,14 @@ * 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 @@ } 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 @@ * 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,6 +144,7 @@ nlmsg_report_cookie(npt, nla_cookie); } +#if 0 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) @@ -171,13 +172,14 @@ 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, +nl_create_generic(struct nl_parsed_link *lattrs, const struct nlattr_bmask *bm, struct nlpcb *nlp, struct nl_pstate *npt) { struct ifc_data ifd = {}; @@ -300,5 +302,6 @@ rtnl_iface_add_cloner(&vlan_cloner); NL_VERIFY_PARSERS(all_parsers); } +#endif diff --git a/sys/netlink/route/route_var.h b/sys/netlink/route/route_var.h --- a/sys/netlink/route/route_var.h +++ b/sys/netlink/route/route_var.h @@ -70,11 +70,18 @@ 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, diff --git a/tests/atf_python/sys/net/netlink.py b/tests/atf_python/sys/net/netlink.py --- a/tests/atf_python/sys/net/netlink.py +++ b/tests/atf_python/sys/net/netlink.py @@ -1082,6 +1082,7 @@ AttrDescr(IflattrType.IFLA_BROADCAST, NlAttrMac), AttrDescr(IflattrType.IFLA_IFNAME, NlAttrStr), AttrDescr(IflattrType.IFLA_MTU, NlAttrU32), + AttrDescr(IflattrType.IFLA_LINK, NlAttrU32), AttrDescr(IflattrType.IFLA_PROMISCUITY, NlAttrU32), AttrDescr(IflattrType.IFLA_OPERSTATE, NlAttrU8), AttrDescr(IflattrType.IFLA_CARRIER, NlAttrU8), @@ -1279,8 +1280,8 @@ val = v["ad"].cls.from_bytes(data[off : off + nla_len], v["ad"].val) if "child" in v: # nested - attrs, _ = self.parse_attrs(data[off : off + nla_len], v["child"]) - val = NlAttrNested(raw_nla_type, attrs) + attrs, _ = self.parse_attrs(data[off + 4 : off + nla_len], v["child"]) + val = NlAttrNested(v["ad"].val, attrs) else: # unknown attribute val = NlAttr(raw_nla_type, data[off + 4 : off + nla_len]) diff --git a/tests/sys/netlink/test_rtnl_iface.py b/tests/sys/netlink/test_rtnl_iface.py --- a/tests/sys/netlink/test_rtnl_iface.py +++ b/tests/sys/netlink/test_rtnl_iface.py @@ -19,6 +19,7 @@ from atf_python.sys.net.netlink import NlRtMsgType from atf_python.sys.net.netlink 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 @@ 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 @@ 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()