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,26 @@ 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 @@ 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 @@ -33,6 +33,8 @@ * $FreeBSD$ */ +#include "opt_netlink.h" + #include #include #include @@ -52,6 +54,11 @@ #include #include +#include +#include +#include +#include + /* Current IF_MAXUNIT expands maximum to 5 characters. */ #define IFCLOSIZ (IFNAMSIZ - 5) @@ -77,6 +84,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,8 +115,8 @@ 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 @@ * 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 @@ 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 @@ * 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 @@ 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->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 @@ 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_vlan.c b/sys/net/if_vlan.c --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -48,6 +48,7 @@ #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 @@ #include #endif +#include +#include +#include +#include + #define VLAN_DEF_HWIDTH 4 #define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST) @@ -320,6 +326,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 +907,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 +946,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 " @@ -981,7 +996,7 @@ 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/netlink_glue.c b/sys/netlink/netlink_glue.c --- a/sys/netlink/netlink_glue.c +++ b/sys/netlink/netlink_glue.c @@ -177,6 +177,19 @@ 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 @@ .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 @@ { 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 --- a/sys/netlink/netlink_module.c +++ b/sys/netlink/netlink_module.c @@ -43,6 +43,7 @@ #include #include #include +#include #include diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h --- a/sys/netlink/netlink_var.h +++ b/sys/netlink/netlink_var.h @@ -172,6 +172,11 @@ #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 @@ 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 --- a/sys/netlink/route/iface.c +++ b/sys/netlink/route/iface.c @@ -303,13 +303,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); @@ -353,7 +347,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 }, @@ -545,21 +539,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 @@ -602,31 +591,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); } @@ -1067,7 +1045,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,161 +144,3 @@ 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); -} - - 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 @@ -71,11 +71,17 @@ 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/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.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 @@ 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()