Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145622929
D54443.id172201.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D54443.id172201.diff
View Options
diff --git a/sys/net/if_gre.h b/sys/net/if_gre.h
--- a/sys/net/if_gre.h
+++ b/sys/net/if_gre.h
@@ -109,6 +109,7 @@
CK_LIST_ENTRY(gre_softc) chain;
CK_LIST_ENTRY(gre_softc) srchash;
};
+
MALLOC_DECLARE(M_GRE);
#ifndef GRE_HASH_SIZE
diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c
--- a/sys/net/if_gre.c
+++ b/sys/net/if_gre.c
@@ -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 @@
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_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 @@
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 @@
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_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 @@
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 @@
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 @@
}
#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_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 @@
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 @@
}
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 @@
}
+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:
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -246,6 +246,15 @@
};
#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 @@
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
diff --git a/tests/sys/netlink/Makefile b/tests/sys/netlink/Makefile
--- a/tests/sys/netlink/Makefile
+++ b/tests/sys/netlink/Makefile
@@ -5,6 +5,7 @@
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
diff --git a/tests/sys/netlink/test_rtnl_gre.c b/tests/sys/netlink/test_rtnl_gre.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_gre.c
@@ -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());
+}
+
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Feb 23, 7:45 AM (5 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28953214
Default Alt Text
D54443.id172201.diff (22 KB)
Attached To
Mode
D54443: if_gre: Add netlink support with tests
Attached
Detach File
Event Timeline
Log In to Comment