Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 31 Jul 2026 12:37:20 +0000
From:      Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 9bfb78bf6357 - main - if_gif: Add netlink support with tests
Message-ID:  <6a6c9700.448a4.7dd9f75d@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by pouria:

URL: https://cgit.FreeBSD.org/src/commit/?id=9bfb78bf6357f8fc165f85a65f8a8e4048282401

commit 9bfb78bf6357f8fc165f85a65f8a8e4048282401
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-07-29 08:46:30 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-07-31 12:36:43 +0000

    if_gif: Add netlink support with tests
    
    Migrate to new if_clone KPI and implement netlink support
    for gif(4). Also break GIFSOPTS ioctl logic out of gif_ioctl.
    
    Reviewed by: markj
    Differential Revision: https://reviews.freebsd.org/D57666
---
 sys/net/if_gif.c                  | 304 +++++++++++++++++++++++++++++++++-----
 sys/netlink/route/interface.h     |  10 ++
 tests/sys/netlink/Makefile        |   1 +
 tests/sys/netlink/test_rtnl_gif.c | 160 ++++++++++++++++++++
 4 files changed, 441 insertions(+), 34 deletions(-)

diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c
index 71bc8e1b64ce..3b5a641af0c6 100644
--- a/sys/net/if_gif.c
+++ b/sys/net/if_gif.c
@@ -90,6 +90,12 @@
 #include <net/if_bridgevar.h>
 #include <net/if_gif.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 <security/mac/mac_framework.h>
 
 static const char gifname[] = "gif";
@@ -97,6 +103,7 @@ static const char gifname[] = "gif";
 MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface");
 static struct sx gif_ioctl_sx;
 SX_SYSINIT(gif_ioctl_sx, &gif_ioctl_sx, "gif_ioctl");
+#define GIF_LOCK_ASSERT() sx_assert(&gif_ioctl_sx, SA_XLOCKED)
 
 void	(*ng_gif_input_p)(struct ifnet *ifp, struct mbuf **mp, int af);
 void	(*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af);
@@ -110,8 +117,18 @@ static void	gif_delete_tunnel(struct gif_softc *);
 static int	gif_ioctl(struct ifnet *, u_long, caddr_t);
 static int	gif_transmit(struct ifnet *, struct mbuf *);
 static void	gif_qflush(struct ifnet *);
-static int	gif_clone_create(struct if_clone *, int, caddr_t);
-static void	gif_clone_destroy(struct ifnet *);
+static int	gif_clone_create(struct if_clone *, char *, size_t,
+		    struct ifc_data *, struct ifnet **);
+static int	gif_clone_destroy(struct if_clone *, struct ifnet *, uint32_t);
+static int	gif_clone_create_nl(struct if_clone *, char *, size_t,
+		    struct ifc_data_nl *);
+static int	gif_clone_modify_nl(struct ifnet *, struct ifc_data_nl *);
+static void	gif_clone_dump_nl(struct ifnet *, struct nl_writer *);
+
+static int	gif_set_addr_nl(struct gif_softc *, struct nl_pstate *,
+		    struct sockaddr *, struct sockaddr *);
+static int	gif_set_flags_nl(struct gif_softc *, struct nl_pstate *,
+		    uint32_t);
 VNET_DEFINE_STATIC(struct if_clone *, gif_cloner);
 #define	V_gif_cloner	VNET(gif_cloner)
 
@@ -134,8 +151,145 @@ VNET_DEFINE_STATIC(int, max_gif_nesting) = MAX_GIF_NEST;
 SYSCTL_INT(_net_link_gif, OID_AUTO, max_nesting, CTLFLAG_VNET | CTLFLAG_RW,
     &VNET_NAME(max_gif_nesting), 0, "Max nested tunnels");
 
+struct nl_parsed_gif {
+	struct sockaddr		*ifla_local;
+	struct sockaddr		*ifla_remote;
+	uint32_t		ifla_flags;
+};
+
+#define _OUT(_field)   offsetof(struct nl_parsed_gif, _field)
+static const struct nlattr_parser nla_p_gif[] = {
+	{ .type = IFLA_IPTUN_LOCAL, .off = _OUT(ifla_local), .cb = nlattr_get_ip },
+	{ .type = IFLA_IPTUN_REMOTE, .off = _OUT(ifla_remote), .cb = nlattr_get_ip },
+	{ .type = IFLA_IPTUN_FLAGS, .off = _OUT(ifla_flags), .cb = nlattr_get_uint32 },
+};
+#undef _OUT
+NL_DECLARE_ATTR_PARSER(gif_modify_parser, nla_p_gif);
+
+static const struct nlhdr_parser *all_parsers[] = {
+       &gif_modify_parser,
+};
+
+static int
+gif_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 (gif_clone_create(ifc, name, len, &ifd_new, &ifd->ifp));
+}
+
+static int
+gif_clone_modify_nl(struct ifnet *ifp, struct ifc_data_nl *ifd)
+{
+	struct gif_softc *sc;
+	struct nl_parsed_link *lattrs = ifd->lattrs;
+	struct nl_pstate *npt = ifd->npt;
+	struct nl_parsed_gif params = {};
+	struct nlattr *attrs = lattrs->ifla_idata;
+	struct nlattr_bmask bm;
+	int error;
+
+	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_GIF);
+	if (error)
+		return (error);
+
+	nl_get_attrs_bmask_raw(NLA_DATA(attrs), NLA_DATA_LEN(attrs), &bm);
+	if ((error = nl_parse_nested(attrs, &gif_modify_parser, npt, &params)) != 0)
+		return (error);
+
+	sx_xlock(&gif_ioctl_sx);
+	sc = ifp->if_softc;
+	if (sc == NULL)
+		goto generic;
+
+	if (nl_has_attr(&bm, IFLA_IPTUN_LOCAL) && nl_has_attr(&bm, IFLA_IPTUN_REMOTE))
+		error = gif_set_addr_nl(sc, npt, params.ifla_local, params.ifla_remote);
+	else if (nl_has_attr(&bm, IFLA_IPTUN_LOCAL) || nl_has_attr(&bm, IFLA_IPTUN_REMOTE)) {
+		error = EINVAL;
+		nlmsg_report_err_msg(npt, "Specify both remote and local address together");
+	}
+
+	if (error == 0 && nl_has_attr(&bm, IFLA_IPTUN_FLAGS))
+		error = gif_set_flags_nl(sc, npt, params.ifla_flags);
+
+generic:
+	sx_xunlock(&gif_ioctl_sx);
+
+	if (error == 0)
+		error = nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt);
+
+	return (error);
+}
+
+static void
+gif_clone_dump_nl(struct ifnet *ifp, struct nl_writer *nw)
+{
+	struct gif_softc *sc;
+	int off, off2;
+
+	nlattr_add_u32(nw, IFLA_LINK, ifp->if_index);
+	nlattr_add_string(nw, IFLA_IFNAME, ifp->if_xname);
+
+	off = nlattr_add_nested(nw, IFLA_LINKINFO);
+	if (off == 0)
+		return;
+
+	nlattr_add_string(nw, IFLA_INFO_KIND, gifname);
+	off2 = nlattr_add_nested(nw, IFLA_INFO_DATA);
+	if (off2 == 0) {
+		nlattr_set_len(nw, off);
+		return;
+	}
+
+	sx_slock(&gif_ioctl_sx);
+	sc = ifp->if_softc;
+	if (sc == NULL)
+		goto ret;
+
+	if (sc->gif_family == AF_INET) {
+#ifdef INET
+		struct in_aliasreq in;
+		if (in_gif_ioctl(sc, SIOCGIFPSRCADDR, (caddr_t)&in) == 0)
+			nlattr_add_in_addr(nw, IFLA_IPTUN_LOCAL,
+			    &in.ifra_addr.sin_addr);
+		if (in_gif_ioctl(sc, SIOCGIFPDSTADDR, (caddr_t)&in) == 0)
+			nlattr_add_in_addr(nw, IFLA_IPTUN_REMOTE,
+			    &in.ifra_addr.sin_addr);
+#endif
+	} else if (sc->gif_family == AF_INET6) {
+#ifdef INET6
+		struct in6_aliasreq in6;
+		if (in6_gif_ioctl(sc, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6) == 0)
+			nlattr_add_in6_addr(nw, IFLA_IPTUN_LOCAL,
+			    &in6.ifra_addr.sin6_addr);
+		if (in6_gif_ioctl(sc, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6) == 0)
+			nlattr_add_in6_addr(nw, IFLA_IPTUN_REMOTE,
+			    &in6.ifra_addr.sin6_addr);
+#endif
+	}
+
+	nlattr_add_u32(nw, IFLA_IPTUN_FLAGS, sc->gif_options);
+
+ret:
+	nlattr_set_len(nw, off2);
+	nlattr_set_len(nw, off);
+
+	sx_sunlock(&gif_ioctl_sx);
+}
+
 static int
-gif_clone_create(struct if_clone *ifc, int unit, caddr_t params)
+gif_clone_create(struct if_clone *ifc, char *name, size_t len,
+    struct ifc_data *ifd, struct ifnet **ifpp)
 {
 	struct gif_softc *sc;
 
@@ -143,7 +297,7 @@ gif_clone_create(struct if_clone *ifc, int unit, caddr_t params)
 	sc->gif_fibnum = curthread->td_proc->p_fibnum;
 	GIF2IFP(sc) = if_alloc(IFT_GIF);
 	GIF2IFP(sc)->if_softc = sc;
-	if_initname(GIF2IFP(sc), gifname, unit);
+	if_initname(GIF2IFP(sc), gifname, ifd->unit);
 
 	GIF2IFP(sc)->if_addrlen = 0;
 	GIF2IFP(sc)->if_mtu    = GIF_MTU;
@@ -161,6 +315,7 @@ gif_clone_create(struct if_clone *ifc, int unit, caddr_t params)
 	bpfattach(GIF2IFP(sc), DLT_NULL, sizeof(u_int32_t));
 	if (ng_gif_attach_p != NULL)
 		(*ng_gif_attach_p)(GIF2IFP(sc));
+	*ifpp = GIF2IFP(sc);
 
 	return (0);
 }
@@ -180,32 +335,42 @@ gif_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused,
 }
 #endif /* VIMAGE */
 
-static void
-gif_clone_destroy(struct ifnet *ifp)
+static int
+gif_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags)
 {
 	struct gif_softc *sc;
 
 	sx_xlock(&gif_ioctl_sx);
 	sc = ifp->if_softc;
 	gif_delete_tunnel(sc);
+	ifp->if_softc = NULL;
+	sx_xunlock(&gif_ioctl_sx);
 	if (ng_gif_detach_p != NULL)
 		(*ng_gif_detach_p)(ifp);
 	bpfdetach(ifp);
 	if_detach(ifp);
-	ifp->if_softc = NULL;
-	sx_xunlock(&gif_ioctl_sx);
 
 	GIF_WAIT();
 	if_free(ifp);
 	free(sc, M_GIF);
+
+	return (0);
 }
 
 static void
 vnet_gif_init(const void *unused __unused)
 {
-
-	V_gif_cloner = if_clone_simple(gifname, gif_clone_create,
-	    gif_clone_destroy, 0);
+	struct if_clone_addreq_v2 req = {
+		.version = 2,
+		.flags = IFC_F_AUTOUNIT,
+		.match_f = NULL,
+		.create_f = gif_clone_create,
+		.destroy_f = gif_clone_destroy,
+		.create_nl_f = gif_clone_create_nl,
+		.modify_nl_f = gif_clone_modify_nl,
+		.dump_nl_f = gif_clone_dump_nl,
+	};
+	V_gif_cloner = ifc_attach_cloner(gifname, (struct if_clone_addreq *)&req);
 #ifdef INET
 	in_gif_init();
 #endif
@@ -220,7 +385,7 @@ static void
 vnet_gif_uninit(const void *unused __unused)
 {
 
-	if_clone_detach(V_gif_cloner);
+	ifc_detach_cloner(V_gif_cloner);
 #ifdef INET
 	in_gif_uninit();
 #endif
@@ -237,6 +402,8 @@ gifmodevent(module_t mod, int type, void *data)
 
 	switch (type) {
 	case MOD_LOAD:
+		NL_VERIFY_PARSERS(all_parsers);
+		break;
 	case MOD_UNLOAD:
 		break;
 	default:
@@ -566,6 +733,37 @@ drop:
 	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
 }
 
+static int
+gif_set_flags(struct gif_softc *sc, u_int opt)
+{
+	int error = 0;
+
+	GIF_LOCK_ASSERT();
+
+	if (opt & ~GIF_OPTMASK)
+		return (EINVAL);
+	if (sc->gif_options == opt)
+		return (0);
+
+	switch (sc->gif_family) {
+#ifdef INET
+	case AF_INET:
+		error = in_gif_setopts(sc, opt);
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		error = in6_gif_setopts(sc, opt);
+		break;
+#endif
+	default:
+		/* No need to invoke AF-handler */
+		sc->gif_options = opt;
+	}
+
+	return (error);
+}
+
 static int
 gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
@@ -640,27 +838,7 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 		    sizeof(options));
 		if (error)
 			break;
-		if (options & ~GIF_OPTMASK) {
-			error = EINVAL;
-			break;
-		}
-		if (sc->gif_options != options) {
-			switch (sc->gif_family) {
-#ifdef INET
-			case AF_INET:
-				error = in_gif_setopts(sc, options);
-				break;
-#endif
-#ifdef INET6
-			case AF_INET6:
-				error = in6_gif_setopts(sc, options);
-				break;
-#endif
-			default:
-				/* No need to invoke AF-handler */
-				sc->gif_options = options;
-			}
-		}
+		error = gif_set_flags(sc, options);
 		break;
 	default:
 		error = EINVAL;
@@ -687,7 +865,7 @@ static void
 gif_delete_tunnel(struct gif_softc *sc)
 {
 
-	sx_assert(&gif_ioctl_sx, SA_XLOCKED);
+	GIF_LOCK_ASSERT();
 	if (sc->gif_family != 0) {
 		CK_LIST_REMOVE(sc, srchash);
 		CK_LIST_REMOVE(sc, chain);
@@ -699,3 +877,61 @@ gif_delete_tunnel(struct gif_softc *sc)
 	GIF2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
 	if_link_state_change(GIF2IFP(sc), LINK_STATE_DOWN);
 }
+
+static int
+gif_set_addr_nl(struct gif_softc *sc, struct nl_pstate *npt,
+    struct sockaddr *src, struct sockaddr *dst)
+{
+#if defined(INET) || defined(INET6)
+	union {
+#ifdef INET
+		struct in_aliasreq in;
+#endif
+#ifdef INET6
+		struct in6_aliasreq in6;
+#endif
+	} aliasreq;
+#endif
+	int error;
+
+	/* XXX: this sanity check runs again in in[6]_gif_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));
+		error = in_gif_ioctl(sc, SIOCSIFPHYADDR, (caddr_t)&aliasreq.in);
+	}
+#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));
+		error = in6_gif_ioctl(sc, SIOCSIFPHYADDR_IN6, (caddr_t)&aliasreq.in6);
+	}
+#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
+gif_set_flags_nl(struct gif_softc *sc, struct nl_pstate *npt, uint32_t opt)
+{
+	int error;
+
+	error = gif_set_flags(sc, opt);
+	if (error == EINVAL)
+		nlmsg_report_err_msg(npt, "gif flags are invalid");
+
+	return (error);
+}
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
index e6aef3d702d8..c018a0adb20d 100644
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -272,6 +272,16 @@ struct ifla_vlan_flags {
 	uint32_t mask;
 };
 
+/* IFLA_INFO_DATA gif attributes */
+enum {
+	IFLA_IPTUN_UNSPEC,
+	IFLA_IPTUN_LOCAL,
+	IFLA_IPTUN_REMOTE,
+	IFLA_IPTUN_FLAGS,
+	__IFLA_IPTUN_MAX,
+};
+#define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
+
 /* IFLA_INFO_DATA gre attributes */
 enum {
 	IFLA_GRE_UNSPEC,
diff --git a/tests/sys/netlink/Makefile b/tests/sys/netlink/Makefile
index 89dba16c823c..5a9ba359078a 100644
--- a/tests/sys/netlink/Makefile
+++ b/tests/sys/netlink/Makefile
@@ -6,6 +6,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_C+=	test_rtnl_gif
 ATF_TESTS_C+=	test_rtnl_route
 ATF_TESTS_PYTEST +=	test_nl_core.py
 ATF_TESTS_PYTEST +=	test_rtnl_iface.py
diff --git a/tests/sys/netlink/test_rtnl_gif.c b/tests/sys/netlink/test_rtnl_gif.c
new file mode 100644
index 000000000000..c29f3911036f
--- /dev/null
+++ b/tests/sys/netlink/test_rtnl_gif.c
@@ -0,0 +1,160 @@
+/*
+ * 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_gif.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_gif {
+	struct sockaddr		*ifla_local;
+	struct sockaddr		*ifla_remote;
+	uint32_t		ifla_flags;
+};
+
+struct nla_gif_info {
+	const char		*kind;
+	struct nl_parsed_gif	data;
+};
+
+struct nla_gif_link {
+	uint32_t		ifi_index;
+	struct nla_gif_info	linkinfo;
+};
+
+#define _OUT(_field)	offsetof(struct nl_parsed_gif, _field)
+static const struct snl_attr_parser nla_p_gif[] = {
+	{ .type = IFLA_IPTUN_LOCAL, .off = _OUT(ifla_local), .cb = snl_attr_get_ip },
+	{ .type = IFLA_IPTUN_REMOTE, .off = _OUT(ifla_remote), .cb = snl_attr_get_ip },
+	{ .type = IFLA_IPTUN_FLAGS, .off = _OUT(ifla_flags), .cb = snl_attr_get_uint32 },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER(gif_linkinfo_data_parser, nla_p_gif);
+
+#define _OUT(_field)	offsetof(struct nla_gif_info, _field)
+static const struct snl_attr_parser ap_gif_linkinfo[] = {
+	{ .type = IFLA_INFO_KIND, .off = _OUT(kind), .cb = snl_attr_get_string },
+	{ .type = IFLA_INFO_DATA, .off = _OUT(data),
+		.arg = &gif_linkinfo_data_parser, .cb = snl_attr_get_nested },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER(gif_linkinfo_parser, ap_gif_linkinfo);
+
+#define _IN(_field)	offsetof(struct ifinfomsg, _field)
+#define _OUT(_field)	offsetof(struct nla_gif_link, _field)
+static const struct snl_attr_parser ap_gif_link[] = {
+	{ .type = IFLA_LINKINFO, .off = _OUT(linkinfo),
+		.arg = &gif_linkinfo_parser, .cb = snl_attr_get_nested },
+};
+
+static const struct snl_field_parser fp_gif_link[] = {
+	{ .off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(gif_parser, struct ifinfomsg, fp_gif_link, ap_gif_link);
+
+ATF_TC(test_rtnl_gif);
+ATF_TC_HEAD(test_rtnl_gif, tc)
+{
+	atf_tc_set_md_var(tc, "descr", "test gif interface using netlink");
+	atf_tc_set_md_var(tc, "require.user", "root");
+	atf_tc_set_md_var(tc, "require.kmods", "netlink if_gif");
+}
+
+ATF_TC_BODY(test_rtnl_gif, tc)
+{
+	struct snl_state ss;
+	struct snl_writer nw;
+	struct nlmsghdr *hdr, *rx_hdr;
+	struct in_addr src, dst;
+	struct nla_gif_link lattrs = {};
+	struct nl_parsed_gif attrs = {};
+	struct snl_errmsg_data e = {};
+	struct ifinfomsg *ifmsg;
+	int off, off2;
+
+	ATF_REQUIRE_MSG(snl_init(&ss, NETLINK_ROUTE), "snl_init() failed");
+
+	/* Create gif 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, "gif10");
+	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+        snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gif");
+	off2 = snl_add_msg_attr_nested(&nw, IFLA_INFO_DATA);
+
+	inet_pton(AF_INET, "127.0.0.1", &src);
+	inet_pton(AF_INET, "127.0.0.2", &dst);
+	snl_add_msg_attr_ip4(&nw, IFLA_IPTUN_LOCAL, &src);
+	snl_add_msg_attr_ip4(&nw, IFLA_IPTUN_REMOTE, &dst);
+	snl_add_msg_attr_u32(&nw, IFLA_IPTUN_FLAGS, (GIF_NOCLAMP | GIF_IGNORE_SOURCE));
+
+	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 gif 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, "gif10");
+	off = snl_add_msg_attr_nested(&nw, IFLA_LINKINFO);
+        snl_add_msg_attr_string(&nw, IFLA_INFO_KIND, "gif");
+	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, &gif_parser, &lattrs));
+	attrs = lattrs.linkinfo.data;
+	ATF_CHECK_STREQ(lattrs.linkinfo.kind, "gif");
+	ATF_CHECK_INTEQ(attrs.ifla_flags, (GIF_NOCLAMP | GIF_IGNORE_SOURCE));
+
+	/* Delete gif 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_gif);
+
+	return (atf_no_error());
+}
+


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6c9700.448a4.7dd9f75d>