From 4491a56efd8f818e223cac9e03c0f6c8bf165ee5 Mon Sep 17 00:00:00 2001
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed, 31 Jan 2024 13:40:35 +0100
Subject: [PATCH 4/7] gtp: add flags to gtp_tunnel object

Flags tells us what fields have been set in this object.

This is required by

	./gtp-tunnel del

otherwise, build helper function adds the MS/UE address and kernel reports
ENOENT, and tunnel identifier is ignored.

Update gtp-tunnel tool to set flowid to zero in the GTP version 0 case,
otherwise kernel reports EINVAL since now this flag is not ever set.
---
 src/gtp-genl.c     | 75 +++++++++++++++++++++++++++-------------------
 src/gtp.c          | 12 ++++++++
 src/internal.h     | 14 +++++++++
 tools/gtp-tunnel.c |  5 ++--
 4 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/src/gtp-genl.c b/src/gtp-genl.c
index 447e1eafa1cb..3b7c3ef0fc68 100644
--- a/src/gtp-genl.c
+++ b/src/gtp-genl.c
@@ -44,42 +44,55 @@
 
 static void gtp_build_payload(struct nlmsghdr *nlh, struct gtp_tunnel *t)
 {
-	mnl_attr_put_u8(nlh, GTPA_FAMILY, t->ms_addr.family);
-	mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version);
-	if (t->ifns >= 0)
+	if (t->flags & GTP_TUN_FAMILY)
+		mnl_attr_put_u8(nlh, GTPA_FAMILY, t->ms_addr.family);
+	if (t->flags & GTP_TUN_VERSION)
+		mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version);
+	if (t->flags & GTP_TUN_IFNS)
 		mnl_attr_put_u32(nlh, GTPA_NET_NS_FD, t->ifns);
-	mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx);
-
-	switch (t->ms_addr.family) {
-	case AF_INET:
-		mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.ip4.s_addr);
-		break;
-	case AF_INET6:
-		mnl_attr_put(nlh, GTPA_MS_ADDR6, sizeof(t->ms_addr.ip6), &t->ms_addr.ip6);
-		break;
-	default:
-		/* No addr is set when deleting a tunnel */
-		break;
+	if (t->flags & GTP_TUN_IFIDX)
+		mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx);
+
+	if (t->flags & GTP_TUN_MS_ADDR) {
+		switch (t->ms_addr.family) {
+		case AF_INET:
+			mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.ip4.s_addr);
+			break;
+		case AF_INET6:
+			mnl_attr_put(nlh, GTPA_MS_ADDR6, sizeof(t->ms_addr.ip6), &t->ms_addr.ip6);
+			break;
+		default:
+			/* No addr is set when deleting a tunnel */
+			break;
+		}
 	}
 
-	switch (t->sgsn_addr.family) {
-	case AF_INET:
-		mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.ip4.s_addr);
-		break;
-	case AF_INET6:
-		mnl_attr_put(nlh, GTPA_PEER_ADDR6, sizeof(t->sgsn_addr.ip6), &t->sgsn_addr.ip6);
-		break;
-	default:
-		/* No addr is set when deleting a tunnel */
-		break;
+	if (t->flags & GTP_TUN_SGSN_ADDR) {
+		switch (t->sgsn_addr.family) {
+		case AF_INET:
+			mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.ip4.s_addr);
+			break;
+		case AF_INET6:
+			mnl_attr_put(nlh, GTPA_PEER_ADDR6, sizeof(t->sgsn_addr.ip6), &t->sgsn_addr.ip6);
+			break;
+		default:
+			/* No addr is set when deleting a tunnel */
+			break;
+		}
 	}
 
-	if (t->gtp_version == GTP_V0) {
-		mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid);
-		mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid);
-	} else if (t->gtp_version == GTP_V1) {
-		mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei);
-		mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei);
+	if (t->flags & GTP_TUN_VERSION) {
+		if (t->gtp_version == GTP_V0) {
+			if (t->flags & GTP_TUN_V0_TID)
+				mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid);
+			if (t->flags & GTP_TUN_V0_FLOWID)
+				mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid);
+		} else if (t->gtp_version == GTP_V1) {
+			if (t->flags & GTP_TUN_V1_I_TEI)
+				mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei);
+			if (t->flags & GTP_TUN_V1_O_TEI)
+				mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei);
+		}
 	}
 }
 
diff --git a/src/gtp.c b/src/gtp.c
index 0c378c407436..af216f7735af 100644
--- a/src/gtp.c
+++ b/src/gtp.c
@@ -49,18 +49,21 @@ EXPORT_SYMBOL(gtp_tunnel_free);
 void gtp_tunnel_set_ifns(struct gtp_tunnel *t, int ifns)
 {
 	t->ifns = ifns;
+	t->flags |= GTP_TUN_IFNS;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_ifns);
 
 void gtp_tunnel_set_ifidx(struct gtp_tunnel *t, uint32_t ifidx)
 {
 	t->ifidx = ifidx;
+	t->flags |= GTP_TUN_IFIDX;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_ifidx);
 
 void gtp_tunnel_set_family(struct gtp_tunnel *t, uint16_t family)
 {
 	t->ms_addr.family = family;
+	t->flags |= GTP_TUN_FAMILY;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_family);
 
@@ -68,6 +71,7 @@ void gtp_tunnel_set_ms_ip4(struct gtp_tunnel *t, struct in_addr *ms_addr)
 {
 	t->ms_addr.family = AF_INET;
 	t->ms_addr.ip4 = *ms_addr;
+	t->flags |= GTP_TUN_FAMILY | GTP_TUN_MS_ADDR;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_ms_ip4);
 
@@ -75,6 +79,7 @@ void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr)
 {
 	t->sgsn_addr.family = AF_INET;
 	t->sgsn_addr.ip4 = *sgsn_addr;
+	t->flags |= GTP_TUN_SGSN_ADDR;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip4);
 
@@ -82,6 +87,7 @@ void gtp_tunnel_set_ms_ip6(struct gtp_tunnel *t, const struct in6_addr *ms_addr)
 {
 	t->ms_addr.family = AF_INET6;
 	t->ms_addr.ip6 = *ms_addr;
+	t->flags |= GTP_TUN_FAMILY | GTP_TUN_MS_ADDR;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_ms_ip6);
 
@@ -89,36 +95,42 @@ void gtp_tunnel_set_sgsn_ip6(struct gtp_tunnel *t, const struct in6_addr *sgsn_a
 {
 	t->sgsn_addr.family = AF_INET6;
 	t->sgsn_addr.ip6 = *sgsn_addr;
+	t->flags |= GTP_TUN_SGSN_ADDR;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip6);
 
 void gtp_tunnel_set_version(struct gtp_tunnel *t, uint32_t version)
 {
 	t->gtp_version = version;
+	t->flags |= GTP_TUN_VERSION;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_version);
 
 void gtp_tunnel_set_tid(struct gtp_tunnel *t, uint64_t tid)
 {
 	t->u.v0.tid = tid;
+	t->flags |= GTP_TUN_V0_TID;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_tid);
 
 void gtp_tunnel_set_flowid(struct gtp_tunnel *t, uint16_t flowid)
 {
 	t->u.v0.flowid = flowid;
+	t->flags |= GTP_TUN_V0_FLOWID;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_flowid);
 
 void gtp_tunnel_set_i_tei(struct gtp_tunnel *t, uint32_t i_tei)
 {
 	t->u.v1.i_tei = i_tei;
+	t->flags |= GTP_TUN_V1_I_TEI;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_i_tei);
 
 void gtp_tunnel_set_o_tei(struct gtp_tunnel *t, uint32_t o_tei)
 {
 	t->u.v1.o_tei = o_tei;
+	t->flags |= GTP_TUN_V1_O_TEI;
 }
 EXPORT_SYMBOL(gtp_tunnel_set_o_tei);
 
diff --git a/src/internal.h b/src/internal.h
index ce8a6832e502..1433621c551b 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -20,6 +20,19 @@ struct gtp_addr {
 	};
 };
 
+enum {
+	GTP_TUN_IFNS		= (1 << 0),
+	GTP_TUN_IFIDX		= (1 << 1),
+	GTP_TUN_FAMILY		= (1 << 2),
+	GTP_TUN_MS_ADDR		= (1 << 3),
+	GTP_TUN_SGSN_ADDR	= (1 << 4),
+	GTP_TUN_VERSION		= (1 << 5),
+	GTP_TUN_V0_TID		= (1 << 6),
+	GTP_TUN_V0_FLOWID	= (1 << 7),
+	GTP_TUN_V1_I_TEI	= (1 << 8),
+	GTP_TUN_V1_O_TEI	= (1 << 9),
+};
+
 struct gtp_tunnel {
 	int             ifns;
 	uint32_t	ifidx;
@@ -36,6 +49,7 @@ struct gtp_tunnel {
 			uint32_t o_tei;
 		} v1;
 	} u;
+	uint32_t	flags;
 };
 
 #endif
diff --git a/tools/gtp-tunnel.c b/tools/gtp-tunnel.c
index 1b8a7a0945f6..8c8f95ffde3c 100644
--- a/tools/gtp-tunnel.c
+++ b/tools/gtp-tunnel.c
@@ -109,9 +109,10 @@ add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
 
 	optidx++;
 
-	if (gtp_version == GTP_V0)
+	if (gtp_version == GTP_V0) {
 		gtp_tunnel_set_tid(t, atoi(argv[optidx++]));
-	else if (gtp_version == GTP_V1) {
+		gtp_tunnel_set_flowid(t, 0);
+	} else if (gtp_version == GTP_V1) {
 		gtp_tunnel_set_i_tei(t, atoi(argv[optidx++]));
 		gtp_tunnel_set_o_tei(t, atoi(argv[optidx++]));
 	}
-- 
2.30.2

