if_gre: Add netlink support with tests
Migrate to new if_clone KPI and implement netlink support for gre(4). Also refactor some of the gre specific ioctls. Reviewed by: glebius, zlei Differential Revision: https://reviews.freebsd.org/D54443
This commit is contained in:
+407
-66
@@ -90,6 +90,12 @@
|
||||
#include <net/bpf.h>
|
||||
#include <net/if_gre.h>
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_ctl.h>
|
||||
#include <netlink/netlink_var.h>
|
||||
#include <netlink/netlink_route.h>
|
||||
#include <netlink/route/route_var.h>
|
||||
|
||||
#include <machine/in_cksum.h>
|
||||
#include <security/mac/mac_framework.h>
|
||||
|
||||
@@ -100,9 +106,16 @@ MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation");
|
||||
|
||||
static struct sx gre_ioctl_sx;
|
||||
SX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl");
|
||||
#define GRE_LOCK_ASSERT() sx_assert(&gre_ioctl_sx, SA_XLOCKED);
|
||||
|
||||
static int gre_clone_create(struct if_clone *, int, caddr_t);
|
||||
static void gre_clone_destroy(struct ifnet *);
|
||||
static int gre_clone_create(struct if_clone *, char *, size_t,
|
||||
struct ifc_data *, struct ifnet **);
|
||||
static int gre_clone_destroy(struct if_clone *, struct ifnet *,
|
||||
uint32_t);
|
||||
static int gre_clone_create_nl(struct if_clone *, char *, size_t,
|
||||
struct ifc_data_nl *);
|
||||
static int gre_clone_modify_nl(struct ifnet *, struct ifc_data_nl *);
|
||||
static void gre_clone_dump_nl(struct ifnet *, struct nl_writer *);
|
||||
VNET_DEFINE_STATIC(struct if_clone *, gre_cloner);
|
||||
#define V_gre_cloner VNET(gre_cloner)
|
||||
|
||||
@@ -115,6 +128,18 @@ static int gre_ioctl(struct ifnet *, u_long, caddr_t);
|
||||
static int gre_output(struct ifnet *, struct mbuf *,
|
||||
const struct sockaddr *, struct route *);
|
||||
static void gre_delete_tunnel(struct gre_softc *);
|
||||
static int gre_set_addr_nl(struct gre_softc *, struct nl_pstate *,
|
||||
struct sockaddr *, struct sockaddr *);
|
||||
|
||||
static int gre_set_flags(struct gre_softc *, uint32_t);
|
||||
static int gre_set_key(struct gre_softc *, uint32_t);
|
||||
static int gre_set_udp_sport(struct gre_softc *, uint16_t);
|
||||
static int gre_setopts(struct gre_softc *, u_long, uint32_t);
|
||||
|
||||
static int gre_set_flags_nl(struct gre_softc *, struct nl_pstate *, uint32_t);
|
||||
static int gre_set_key_nl(struct gre_softc *, struct nl_pstate *, uint32_t);
|
||||
static int gre_set_encap_nl(struct gre_softc *, struct nl_pstate *, uint32_t);
|
||||
static int gre_set_udp_sport_nl(struct gre_softc *, struct nl_pstate *, uint16_t);
|
||||
|
||||
SYSCTL_DECL(_net_link);
|
||||
static SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
|
||||
@@ -136,12 +161,46 @@ VNET_DEFINE_STATIC(int, max_gre_nesting) = MAX_GRE_NEST;
|
||||
SYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET,
|
||||
&VNET_NAME(max_gre_nesting), 0, "Max nested tunnels");
|
||||
|
||||
struct nl_parsed_gre {
|
||||
struct sockaddr *ifla_local;
|
||||
struct sockaddr *ifla_remote;
|
||||
uint32_t ifla_flags;
|
||||
uint32_t ifla_okey;
|
||||
uint32_t ifla_encap_type;
|
||||
uint16_t ifla_encap_sport;
|
||||
};
|
||||
|
||||
#define _OUT(_field) offsetof(struct nl_parsed_gre, _field)
|
||||
static const struct nlattr_parser nla_p_gre[] = {
|
||||
{ .type = IFLA_GRE_LOCAL, .off = _OUT(ifla_local), .cb = nlattr_get_ip },
|
||||
{ .type = IFLA_GRE_REMOTE, .off = _OUT(ifla_remote), .cb = nlattr_get_ip },
|
||||
{ .type = IFLA_GRE_FLAGS, .off = _OUT(ifla_flags), .cb = nlattr_get_uint32 },
|
||||
{ .type = IFLA_GRE_OKEY, .off = _OUT(ifla_okey), .cb = nlattr_get_uint32 },
|
||||
{ .type = IFLA_GRE_ENCAP_TYPE, .off = _OUT(ifla_encap_type), .cb = nlattr_get_uint32 },
|
||||
{ .type = IFLA_GRE_ENCAP_SPORT, .off = _OUT(ifla_encap_sport), .cb = nlattr_get_uint16 },
|
||||
};
|
||||
#undef _OUT
|
||||
NL_DECLARE_ATTR_PARSER(gre_modify_parser, nla_p_gre);
|
||||
|
||||
static const struct nlhdr_parser *all_parsers[] = {
|
||||
&gre_modify_parser,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
vnet_gre_init(const void *unused __unused)
|
||||
{
|
||||
|
||||
V_gre_cloner = if_clone_simple(grename, gre_clone_create,
|
||||
gre_clone_destroy, 0);
|
||||
struct if_clone_addreq_v2 req = {
|
||||
.version = 2,
|
||||
.flags = IFC_F_AUTOUNIT,
|
||||
.match_f = NULL,
|
||||
.create_f = gre_clone_create,
|
||||
.destroy_f = gre_clone_destroy,
|
||||
.create_nl_f = gre_clone_create_nl,
|
||||
.modify_nl_f = gre_clone_modify_nl,
|
||||
.dump_nl_f = gre_clone_dump_nl,
|
||||
};
|
||||
V_gre_cloner = ifc_attach_cloner(grename, (struct if_clone_addreq *)&req);
|
||||
#ifdef INET
|
||||
in_gre_init();
|
||||
#endif
|
||||
@@ -156,7 +215,7 @@ static void
|
||||
vnet_gre_uninit(const void *unused __unused)
|
||||
{
|
||||
|
||||
if_clone_detach(V_gre_cloner);
|
||||
ifc_detach_cloner(V_gre_cloner);
|
||||
#ifdef INET
|
||||
in_gre_uninit();
|
||||
#endif
|
||||
@@ -169,7 +228,129 @@ VNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
|
||||
vnet_gre_uninit, NULL);
|
||||
|
||||
static int
|
||||
gre_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
gre_clone_create_nl(struct if_clone *ifc, char *name, size_t len,
|
||||
struct ifc_data_nl *ifd)
|
||||
{
|
||||
struct ifc_data ifd_new = {
|
||||
.flags = IFC_F_SYSSPACE,
|
||||
.unit = ifd->unit,
|
||||
};
|
||||
|
||||
return (gre_clone_create(ifc, name, len, &ifd_new, &ifd->ifp));
|
||||
}
|
||||
|
||||
static int
|
||||
gre_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
|
||||
{
|
||||
struct gre_softc *sc = ifp->if_softc;
|
||||
struct nl_parsed_link *lattrs = ifd->lattrs;
|
||||
struct nl_pstate *npt = ifd->npt;
|
||||
struct nl_parsed_gre params;
|
||||
struct nlattr *attrs = lattrs->ifla_idata;
|
||||
struct nlattr_bmask bm;
|
||||
int error = 0;
|
||||
|
||||
if ((attrs == NULL) ||
|
||||
(nl_has_attr(ifd->bm, IFLA_LINKINFO) == 0)) {
|
||||
error = nl_modify_ifp_generic(ifp, lattrs, ifd->bm, npt);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = priv_check(curthread, PRIV_NET_GRE);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* make sure ignored attributes by nl_parse will not cause panics */
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
nl_get_attrs_bmask_raw(NLA_DATA(attrs), NLA_DATA_LEN(attrs), &bm);
|
||||
if ((error = nl_parse_nested(attrs, &gre_modify_parser, npt, ¶ms)) != 0)
|
||||
return (error);
|
||||
|
||||
if (nl_has_attr(&bm, IFLA_GRE_LOCAL) && nl_has_attr(&bm, IFLA_GRE_REMOTE))
|
||||
error = gre_set_addr_nl(sc, npt, params.ifla_local, params.ifla_remote);
|
||||
else if (nl_has_attr(&bm, IFLA_GRE_LOCAL) || nl_has_attr(&bm, IFLA_GRE_REMOTE)) {
|
||||
error = EINVAL;
|
||||
nlmsg_report_err_msg(npt, "Specify both remote and local address together");
|
||||
}
|
||||
|
||||
if (error == 0 && nl_has_attr(&bm, IFLA_GRE_FLAGS))
|
||||
error = gre_set_flags_nl(sc, npt, params.ifla_flags);
|
||||
|
||||
if (error == 0 && nl_has_attr(&bm, IFLA_GRE_OKEY))
|
||||
error = gre_set_key_nl(sc, npt, params.ifla_okey);
|
||||
|
||||
if (error == 0 && nl_has_attr(&bm, IFLA_GRE_ENCAP_TYPE))
|
||||
error = gre_set_encap_nl(sc, npt, params.ifla_encap_type);
|
||||
|
||||
if (error == 0 && nl_has_attr(&bm, IFLA_GRE_ENCAP_SPORT))
|
||||
error = gre_set_udp_sport_nl(sc, npt, params.ifla_encap_sport);
|
||||
|
||||
if (error == 0)
|
||||
error = nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
gre_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
|
||||
{
|
||||
GRE_RLOCK_TRACKER;
|
||||
struct gre_softc *sc;
|
||||
struct ifreq ifr;
|
||||
|
||||
nlattr_add_u32(nw, IFLA_LINK, ifp->if_index);
|
||||
nlattr_add_string(nw, IFLA_IFNAME, ifp->if_xname);
|
||||
|
||||
int off = nlattr_add_nested(nw, IFLA_LINKINFO);
|
||||
if (off == 0)
|
||||
return;
|
||||
|
||||
nlattr_add_string(nw, IFLA_INFO_KIND, "gre");
|
||||
int off2 = nlattr_add_nested(nw, IFLA_INFO_DATA);
|
||||
if (off2 == 0) {
|
||||
nlattr_set_len(nw, off);
|
||||
return;
|
||||
}
|
||||
|
||||
sc = ifp->if_softc;
|
||||
GRE_RLOCK();
|
||||
|
||||
if (sc->gre_family == AF_INET) {
|
||||
#ifdef INET
|
||||
if (in_gre_ioctl(sc, SIOCGIFPSRCADDR, (caddr_t)&ifr) == 0)
|
||||
nlattr_add_in_addr(nw, IFLA_GRE_LOCAL,
|
||||
(const struct in_addr *)&ifr.ifr_addr);
|
||||
if (in_gre_ioctl(sc, SIOCGIFPDSTADDR, (caddr_t)&ifr) == 0)
|
||||
nlattr_add_in_addr(nw, IFLA_GRE_LOCAL,
|
||||
(const struct in_addr *)&ifr.ifr_dstaddr);
|
||||
#endif
|
||||
} else if (sc->gre_family == AF_INET6) {
|
||||
#ifdef INET6
|
||||
if (in_gre_ioctl(sc, SIOCGIFPSRCADDR_IN6, (caddr_t)&ifr) == 0)
|
||||
nlattr_add_in6_addr(nw, IFLA_GRE_LOCAL,
|
||||
(const struct in6_addr *)&ifr.ifr_addr);
|
||||
if (in_gre_ioctl(sc, SIOCGIFPDSTADDR_IN6, (caddr_t)&ifr) == 0)
|
||||
nlattr_add_in6_addr(nw, IFLA_GRE_LOCAL,
|
||||
(const struct in6_addr *)&ifr.ifr_dstaddr);
|
||||
#endif
|
||||
}
|
||||
|
||||
nlattr_add_u32(nw, IFLA_GRE_FLAGS, sc->gre_options);
|
||||
nlattr_add_u32(nw, IFLA_GRE_OKEY, sc->gre_key);
|
||||
nlattr_add_u32(nw, IFLA_GRE_ENCAP_TYPE,
|
||||
sc->gre_options & GRE_UDPENCAP ? IFLA_TUNNEL_GRE_UDP : IFLA_TUNNEL_NONE);
|
||||
nlattr_add_u16(nw, IFLA_GRE_ENCAP_SPORT, sc->gre_port);
|
||||
|
||||
nlattr_set_len(nw, off2);
|
||||
nlattr_set_len(nw, off);
|
||||
|
||||
GRE_RUNLOCK();
|
||||
}
|
||||
|
||||
static int
|
||||
gre_clone_create(struct if_clone *ifc, char *name, size_t len,
|
||||
struct ifc_data *ifd, struct ifnet **ifpp)
|
||||
{
|
||||
struct gre_softc *sc;
|
||||
|
||||
@@ -177,7 +358,7 @@ gre_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
sc->gre_fibnum = curthread->td_proc->p_fibnum;
|
||||
GRE2IFP(sc) = if_alloc(IFT_TUNNEL);
|
||||
GRE2IFP(sc)->if_softc = sc;
|
||||
if_initname(GRE2IFP(sc), grename, unit);
|
||||
if_initname(GRE2IFP(sc), grename, ifd->unit);
|
||||
|
||||
GRE2IFP(sc)->if_mtu = GREMTU;
|
||||
GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
|
||||
@@ -192,6 +373,8 @@ gre_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
||||
GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE;
|
||||
if_attach(GRE2IFP(sc));
|
||||
bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t));
|
||||
*ifpp = GRE2IFP(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -210,8 +393,8 @@ gre_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused,
|
||||
}
|
||||
#endif /* VIMAGE */
|
||||
|
||||
static void
|
||||
gre_clone_destroy(struct ifnet *ifp)
|
||||
static int
|
||||
gre_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
|
||||
{
|
||||
struct gre_softc *sc;
|
||||
|
||||
@@ -226,8 +409,103 @@ gre_clone_destroy(struct ifnet *ifp)
|
||||
GRE_WAIT();
|
||||
if_free(ifp);
|
||||
free(sc, M_GRE);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_key(struct gre_softc *sc, uint32_t key)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
GRE_LOCK_ASSERT();
|
||||
|
||||
if (sc->gre_key == key)
|
||||
return (0);
|
||||
error = gre_setopts(sc, GRESKEY, key);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_flags(struct gre_softc *sc, uint32_t opt)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
GRE_LOCK_ASSERT();
|
||||
|
||||
if (opt & ~GRE_OPTMASK)
|
||||
return (EINVAL);
|
||||
if (sc->gre_options == opt)
|
||||
return (0);
|
||||
error = gre_setopts(sc, GRESOPTS, opt);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_udp_sport(struct gre_softc *sc, uint16_t port)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
GRE_LOCK_ASSERT();
|
||||
|
||||
if (port != 0 && (port < V_ipport_hifirstauto ||
|
||||
port > V_ipport_hilastauto))
|
||||
return (EINVAL);
|
||||
if (sc->gre_port == port)
|
||||
return (0);
|
||||
if ((sc->gre_options & GRE_UDPENCAP) == 0) {
|
||||
/*
|
||||
* UDP encapsulation is not enabled, thus
|
||||
* there is no need to reattach softc.
|
||||
*/
|
||||
sc->gre_port = port;
|
||||
return (0);
|
||||
}
|
||||
error = gre_setopts(sc, GRESPORT, port);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_setopts(struct gre_softc *sc, u_long cmd, uint32_t opt)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
GRE_LOCK_ASSERT();
|
||||
|
||||
switch (sc->gre_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
error = in_gre_setopts(sc, cmd, opt);
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
error = in6_gre_setopts(sc, cmd, opt);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/*
|
||||
* Tunnel is not yet configured.
|
||||
* We can just change any parameters.
|
||||
*/
|
||||
if (cmd == GRESKEY)
|
||||
sc->gre_key = opt;
|
||||
if (cmd == GRESOPTS)
|
||||
sc->gre_options = opt;
|
||||
if (cmd == GRESPORT)
|
||||
sc->gre_port = opt;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* XXX: Do we need to initiate change of interface
|
||||
* state here?
|
||||
*/
|
||||
return (error);
|
||||
};
|
||||
|
||||
static int
|
||||
gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
{
|
||||
@@ -303,61 +581,12 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||
if ((error = copyin(ifr_data_get_ptr(ifr), &opt,
|
||||
sizeof(opt))) != 0)
|
||||
break;
|
||||
if (cmd == GRESKEY) {
|
||||
if (sc->gre_key == opt)
|
||||
break;
|
||||
} else if (cmd == GRESOPTS) {
|
||||
if (opt & ~GRE_OPTMASK) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (sc->gre_options == opt)
|
||||
break;
|
||||
} else if (cmd == GRESPORT) {
|
||||
if (opt != 0 && (opt < V_ipport_hifirstauto ||
|
||||
opt > V_ipport_hilastauto)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (sc->gre_port == opt)
|
||||
break;
|
||||
if ((sc->gre_options & GRE_UDPENCAP) == 0) {
|
||||
/*
|
||||
* UDP encapsulation is not enabled, thus
|
||||
* there is no need to reattach softc.
|
||||
*/
|
||||
sc->gre_port = opt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (sc->gre_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
error = in_gre_setopts(sc, cmd, opt);
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
error = in6_gre_setopts(sc, cmd, opt);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/*
|
||||
* Tunnel is not yet configured.
|
||||
* We can just change any parameters.
|
||||
*/
|
||||
if (cmd == GRESKEY)
|
||||
sc->gre_key = opt;
|
||||
if (cmd == GRESOPTS)
|
||||
sc->gre_options = opt;
|
||||
if (cmd == GRESPORT)
|
||||
sc->gre_port = opt;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* XXX: Do we need to initiate change of interface
|
||||
* state here?
|
||||
*/
|
||||
if (cmd == GRESKEY)
|
||||
error = gre_set_key(sc, opt);
|
||||
else if (cmd == GRESOPTS)
|
||||
error = gre_set_flags(sc, opt);
|
||||
else if (cmd == GRESPORT)
|
||||
error = gre_set_udp_sport(sc, opt);
|
||||
break;
|
||||
case GREGKEY:
|
||||
error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr),
|
||||
@@ -539,7 +768,7 @@ gre_input(struct mbuf *m, int off, int proto, void *arg)
|
||||
}
|
||||
if (flags & GRE_FLAGS_KP) {
|
||||
#ifdef notyet
|
||||
/*
|
||||
/*
|
||||
* XXX: The current implementation uses the key only for outgoing
|
||||
* packets. But we can check the key value here, or even in the
|
||||
* encapcheck function.
|
||||
@@ -808,12 +1037,124 @@ gre_qflush(struct ifnet *ifp __unused)
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_addr_nl(struct gre_softc *sc, struct nl_pstate *npt,
|
||||
struct sockaddr *src, struct sockaddr *dst)
|
||||
{
|
||||
union {
|
||||
struct in_aliasreq in;
|
||||
struct in6_aliasreq in6;
|
||||
} aliasreq;
|
||||
int error;
|
||||
|
||||
/* XXX: this sanity check runs again in in[6]_gre_ioctl */
|
||||
if (src->sa_family != dst->sa_family)
|
||||
error = EADDRNOTAVAIL;
|
||||
#ifdef INET
|
||||
else if (src->sa_family == AF_INET) {
|
||||
memcpy(&aliasreq.in.ifra_addr, src, sizeof(struct sockaddr_in));
|
||||
memcpy(&aliasreq.in.ifra_dstaddr, dst, sizeof(struct sockaddr_in));
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
error = in_gre_ioctl(sc, SIOCSIFPHYADDR, (caddr_t)&aliasreq.in);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
#endif
|
||||
#ifdef INET6
|
||||
} else if (src->sa_family == AF_INET6) {
|
||||
memcpy(&aliasreq.in6.ifra_addr, src, sizeof(struct sockaddr_in6));
|
||||
memcpy(&aliasreq.in6.ifra_dstaddr, dst, sizeof(struct sockaddr_in6));
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
error = in6_gre_ioctl(sc, SIOCSIFPHYADDR_IN6, (caddr_t)&aliasreq.in6);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
#endif
|
||||
} else
|
||||
error = EAFNOSUPPORT;
|
||||
|
||||
if (error == EADDRNOTAVAIL)
|
||||
nlmsg_report_err_msg(npt, "address is invalid");
|
||||
if (error == EEXIST)
|
||||
nlmsg_report_err_msg(npt, "remote and local addresses are the same");
|
||||
if (error == EAFNOSUPPORT)
|
||||
nlmsg_report_err_msg(npt, "address family is not supported");
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_flags_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t opt)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
error = gre_set_flags(sc, opt);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
|
||||
if (error == EINVAL)
|
||||
nlmsg_report_err_msg(npt, "gre flags are invalid");
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_key_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t key)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
error = gre_set_key(sc, key);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
|
||||
if (error == EINVAL)
|
||||
nlmsg_report_err_msg(npt, "gre key is invalid: %u", key);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
gre_set_encap_nl(struct gre_softc *sc, struct nl_pstate *npt, uint32_t type)
|
||||
{
|
||||
uint32_t opt;
|
||||
int error = 0;
|
||||
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
opt = sc->gre_options;
|
||||
if (type & IFLA_TUNNEL_GRE_UDP)
|
||||
opt |= GRE_UDPENCAP;
|
||||
else
|
||||
opt &= ~GRE_UDPENCAP;
|
||||
error = gre_set_flags(sc, opt);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
|
||||
if (error == EEXIST)
|
||||
nlmsg_report_err_msg(npt, "same gre tunnel exist");
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
gre_set_udp_sport_nl(struct gre_softc *sc, struct nl_pstate *npt, uint16_t port)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
sx_xlock(&gre_ioctl_sx);
|
||||
error = gre_set_udp_sport(sc, port);
|
||||
sx_xunlock(&gre_ioctl_sx);
|
||||
|
||||
if (error == EINVAL)
|
||||
nlmsg_report_err_msg(npt, "source port is invalid: %u", port);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
gremodevent(module_t mod, int type, void *data)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
NL_VERIFY_PARSERS(all_parsers);
|
||||
break;
|
||||
case MOD_UNLOAD:
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -109,6 +109,7 @@ struct gre_softc {
|
||||
CK_LIST_ENTRY(gre_softc) chain;
|
||||
CK_LIST_ENTRY(gre_softc) srchash;
|
||||
};
|
||||
|
||||
MALLOC_DECLARE(M_GRE);
|
||||
|
||||
#ifndef GRE_HASH_SIZE
|
||||
|
||||
@@ -246,6 +246,15 @@ enum {
|
||||
};
|
||||
#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1)
|
||||
|
||||
/* Encapsulation Types */
|
||||
enum {
|
||||
IFLA_TUNNEL_NONE,
|
||||
IFLA_TUNNEL_GRE_UDP, /* GRE UDP Encapsulation */
|
||||
__IFLA_TUNNEL_MAX,
|
||||
};
|
||||
|
||||
#define IFLA_TUNNEL_MAX (__IFLA_TUNNEL_MAX - 1)
|
||||
|
||||
/* IFLA_INFO_DATA vlan attributes */
|
||||
enum {
|
||||
IFLA_VLAN_UNSPEC,
|
||||
@@ -263,4 +272,18 @@ struct ifla_vlan_flags {
|
||||
uint32_t mask;
|
||||
};
|
||||
|
||||
/* IFLA_INFO_DATA gre attributes */
|
||||
enum {
|
||||
IFLA_GRE_UNSPEC,
|
||||
IFLA_GRE_LOCAL,
|
||||
IFLA_GRE_REMOTE,
|
||||
IFLA_GRE_FLAGS,
|
||||
IFLA_GRE_OKEY,
|
||||
IFLA_GRE_ENCAP_TYPE,
|
||||
IFLA_GRE_ENCAP_SPORT,
|
||||
__IFLA_GRE_MAX,
|
||||
};
|
||||
|
||||
#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@ TESTSDIR= ${TESTSBASE}/sys/netlink
|
||||
|
||||
ATF_TESTS_C+= netlink_socket
|
||||
ATF_TESTS_C+= test_snl test_snl_generic
|
||||
ATF_TESTS_C+= test_rtnl_gre
|
||||
ATF_TESTS_PYTEST += test_nl_core.py
|
||||
ATF_TESTS_PYTEST += test_rtnl_iface.py
|
||||
ATF_TESTS_PYTEST += test_rtnl_ifaddr.py
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if_gre.h>
|
||||
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_route.h>
|
||||
#include "netlink/netlink_snl.h"
|
||||
#include <netlink/netlink_snl_route.h>
|
||||
#include <netlink/netlink_snl_route_compat.h>
|
||||
#include <netlink/netlink_snl_route_parsers.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
struct nl_parsed_gre {
|
||||
struct sockaddr *ifla_local;
|
||||
struct sockaddr *ifla_remote;
|
||||
uint32_t ifla_flags;
|
||||
uint32_t ifla_okey;
|
||||
uint32_t ifla_encap_type;
|
||||
uint16_t ifla_encap_sport;
|
||||
};
|
||||
|
||||
struct nla_gre_info {
|
||||
const char *kind;
|
||||
struct nl_parsed_gre data;
|
||||
};
|
||||
|
||||
struct nla_gre_link {
|
||||
uint32_t ifi_index;
|
||||
struct nla_gre_info linkinfo;
|
||||
};
|
||||
|
||||
#define _OUT(_field) offsetof(struct nl_parsed_gre, _field)
|
||||
static const struct snl_attr_parser nla_p_gre[] = {
|
||||
{ .type = IFLA_GRE_LOCAL, .off = _OUT(ifla_local), .cb = snl_attr_get_ip },
|
||||
{ .type = IFLA_GRE_REMOTE, .off = _OUT(ifla_remote), .cb = snl_attr_get_ip },
|
||||
{ .type = IFLA_GRE_FLAGS, .off = _OUT(ifla_flags), .cb = snl_attr_get_uint32 },
|
||||
{ .type = IFLA_GRE_OKEY, .off = _OUT(ifla_okey), .cb = snl_attr_get_uint32 },
|
||||
{ .type = IFLA_GRE_ENCAP_TYPE, .off = _OUT(ifla_encap_type), .cb = snl_attr_get_uint32 },
|
||||
{ .type = IFLA_GRE_ENCAP_SPORT, .off = _OUT(ifla_encap_sport), .cb = snl_attr_get_uint16 },
|
||||
};
|
||||
#undef _OUT
|
||||
SNL_DECLARE_ATTR_PARSER(gre_linkinfo_data_parser, nla_p_gre);
|
||||
|
||||
#define _OUT(_field) offsetof(struct nla_gre_info, _field)
|
||||
static const struct snl_attr_parser ap_gre_linkinfo[] = {
|
||||
{ .type = IFLA_INFO_KIND, .off = _OUT(kind), .cb = snl_attr_get_string },
|
||||
{ .type = IFLA_INFO_DATA, .off = _OUT(data),
|
||||
.arg = &gre_linkinfo_data_parser, .cb = snl_attr_get_nested },
|
||||
};
|
||||
#undef _OUT
|
||||
SNL_DECLARE_ATTR_PARSER(gre_linkinfo_parser, ap_gre_linkinfo);
|
||||
|
||||
#define _IN(_field) offsetof(struct ifinfomsg, _field)
|
||||
#define _OUT(_field) offsetof(struct nla_gre_link, _field)
|
||||
static const struct snl_attr_parser ap_gre_link[] = {
|
||||
{ .type = IFLA_LINKINFO, .off = _OUT(linkinfo),
|
||||
.arg = &gre_linkinfo_parser, .cb = snl_attr_get_nested },
|
||||
};
|
||||
|
||||
static const struct snl_field_parser fp_gre_link[] = {
|
||||
{ .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
|
||||
};
|
||||
#undef _IN
|
||||
#undef _OUT
|
||||
SNL_DECLARE_PARSER(gre_parser, struct ifinfomsg, fp_gre_link, ap_gre_link);
|
||||
|
||||
ATF_TC(test_rtnl_gre);
|
||||
ATF_TC_HEAD(test_rtnl_gre, tc)
|
||||
{
|
||||
atf_tc_set_md_var(tc, "descr", "test gre interface using netlink");
|
||||
atf_tc_set_md_var(tc, "require.user", "root");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(test_rtnl_gre, tc)
|
||||
{
|
||||
struct snl_state ss;
|
||||
struct snl_writer nw;
|
||||
struct nlmsghdr *hdr, *rx_hdr;
|
||||
struct sockaddr_in src, dst;
|
||||
struct nla_gre_link lattrs = {};
|
||||
struct nl_parsed_gre attrs = {};
|
||||
struct snl_errmsg_data e = {};
|
||||
struct ifinfomsg *ifmsg;
|
||||
int off, off2;
|
||||
|
||||
ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
|
||||
|
||||
/* Create gre interface */
|
||||
snl_init_writer(&ss, &nw);
|
||||
ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_NEWLINK)) != NULL);
|
||||
hdr->nlmsg_flags |= (NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST | NLM_F_ACK);
|
||||
snl_reserve_msg_object(&nw, struct ifinfomsg);
|
||||
|
||||
/* Create parameters */
|
||||
snl_add_msg_attr_string(&nw, IFLA_IFNAME, "gre10");
|
||||
off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
|
||||
snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gre");
|
||||
off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
|
||||
|
||||
src.sin_family = AF_INET;
|
||||
dst.sin_family = AF_INET;
|
||||
inet_pton(src.sin_family, "127.0.0.1", &src.sin_addr);
|
||||
inet_pton(dst.sin_family, "127.0.0.2", &dst.sin_addr);
|
||||
snl_add_msg_attr_ip(&nw, IFLA_GRE_LOCAL, (struct sockaddr *)&src);
|
||||
snl_add_msg_attr_ip(&nw, IFLA_GRE_REMOTE, (struct sockaddr *)&dst);
|
||||
snl_add_msg_attr_u32(&nw, IFLA_GRE_FLAGS, (GRE_ENABLE_SEQ | GRE_ENABLE_CSUM));
|
||||
snl_add_msg_attr_u32(&nw, IFLA_GRE_OKEY, 123456);
|
||||
snl_add_msg_attr_u32(&nw, IFLA_GRE_ENCAP_TYPE, IFLA_TUNNEL_GRE_UDP);
|
||||
snl_add_msg_attr_u16(&nw, IFLA_GRE_ENCAP_SPORT, 50000);
|
||||
|
||||
snl_end_attr_nested(&nw, off2);
|
||||
snl_end_attr_nested(&nw, off);
|
||||
|
||||
ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
|
||||
ATF_REQUIRE(snl_send_message(&ss, hdr));
|
||||
ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
|
||||
ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
|
||||
ATF_REQUIRE_INTEQ(e.error, 0);
|
||||
|
||||
/* Dump gre interface */
|
||||
snl_init_writer(&ss, &nw);
|
||||
ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_GETLINK)) != NULL);
|
||||
hdr->nlmsg_flags |= NLM_F_DUMP;
|
||||
snl_reserve_msg_object(&nw, struct ifinfomsg);
|
||||
snl_add_msg_attr_string(&nw, IFLA_IFNAME, "gre10");
|
||||
off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
|
||||
snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gre");
|
||||
snl_end_attr_nested(&nw, off);
|
||||
|
||||
ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
|
||||
ATF_REQUIRE(snl_send_message(&ss, hdr));
|
||||
|
||||
/* Check parameters */
|
||||
ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
|
||||
ATF_CHECK(snl_parse_nlmsg(&ss, rx_hdr, &gre_parser, &lattrs));
|
||||
attrs = lattrs.linkinfo.data;
|
||||
ATF_CHECK_STREQ(lattrs.linkinfo.kind, "gre");
|
||||
ATF_CHECK_INTEQ(attrs.ifla_flags, (GRE_ENABLE_SEQ | GRE_ENABLE_CSUM | GRE_UDPENCAP));
|
||||
ATF_CHECK_INTEQ(attrs.ifla_okey, 123456);
|
||||
ATF_CHECK_INTEQ(attrs.ifla_encap_type, IFLA_TUNNEL_GRE_UDP);
|
||||
ATF_CHECK_INTEQ(attrs.ifla_encap_sport, 50000);
|
||||
|
||||
/* Delete gre interface */
|
||||
snl_init_writer(&ss, &nw);
|
||||
ATF_REQUIRE((hdr = snl_create_msg_request(&nw, RTM_DELLINK)) != NULL);
|
||||
hdr->nlmsg_flags |= (NLM_F_ACK | NLM_F_REQUEST);
|
||||
ATF_REQUIRE((ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg)) != NULL);
|
||||
ifmsg->ifi_index = lattrs.ifi_index;
|
||||
ATF_REQUIRE((hdr = snl_finalize_msg(&nw)) != NULL);
|
||||
ATF_REQUIRE(snl_send_message(&ss, hdr));
|
||||
ATF_REQUIRE((rx_hdr = snl_read_reply(&ss, hdr->nlmsg_seq)) != NULL);
|
||||
ATF_REQUIRE(snl_parse_errmsg(&ss, rx_hdr, &e));
|
||||
ATF_REQUIRE_INTEQ(e.error, 0);
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, test_rtnl_gre);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user