Change in osmo-ggsn[master]: Introduce program gtp-echo-responder

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Fri Oct 8 05:25:48 UTC 2021


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/25652 )

Change subject: Introduce program gtp-echo-responder
......................................................................

Introduce program gtp-echo-responder

This is a small standalone program (under MIT license, hence cannot make
use of libosmocore) whose only purpose is to answer GTPC (v1 and v2)
Echo Request messages with Echo Reply ones, with information provided
from the command line.

A small python script companion is provided to easily test the program.

Related: SYS#5598
Change-Id: Ibdd6d8f6920571db0c60cf8b3b25d541b15ad3f1
---
M Makefile.am
M configure.ac
M contrib/osmo-ggsn.spec.in
M debian/control
M debian/copyright
A debian/gtp-echo-responder.install
A utils/Makefile.am
A utils/gtp_echo_responder.c
A utils/gtp_echo_responder_test.py
9 files changed, 619 insertions(+), 1 deletion(-)

Approvals:
  laforge: Looks good to me, approved; Verified
  osmith: Looks good to me, but someone else must approve



diff --git a/Makefile.am b/Makefile.am
index f431bd9..7fb2529 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
 ## Process this file with automake to produce Makefile.in
-SUBDIRS = lib gtp ggsn sgsnemu doc contrib tests
+SUBDIRS = lib gtp ggsn sgsnemu doc contrib utils tests
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libgtp.pc
diff --git a/configure.ac b/configure.ac
index 2cf44fc..cfc9f21 100644
--- a/configure.ac
+++ b/configure.ac
@@ -257,6 +257,7 @@
                  lib/Makefile
                  intl/Makefile
                  po/Makefile
+                 utils/Makefile
                  sgsnemu/Makefile
                  doc/manuals/Makefile
                  contrib/Makefile
diff --git a/contrib/osmo-ggsn.spec.in b/contrib/osmo-ggsn.spec.in
index 6d55af8..bee8fc1 100644
--- a/contrib/osmo-ggsn.spec.in
+++ b/contrib/osmo-ggsn.spec.in
@@ -61,6 +61,15 @@
 This subpackage contains libraries and header files for developing
 applications that want to make use of libgtp.
 
+%package -n gtp-echo-responder
+Summary:        Small program answering GTP ECHO Request with GTP ECHO Response
+License:        MIT
+Group:          System/Libraries
+
+%description -n gtp-echo-responder
+Small program answering GTP ECHO Request with GTP ECHO Response for both GTPCv1
+and GTPCv2.
+
 %prep
 %setup -q
 
@@ -122,4 +131,7 @@
 %{_libdir}/libgtp.so
 %{_libdir}/pkgconfig/libgtp.pc
 
+%files -n gtp-echo-responder
+%{_bindir}/gtp-echo-responder
+
 %changelog
diff --git a/debian/control b/debian/control
index bbffd49..6d5cde5 100644
--- a/debian/control
+++ b/debian/control
@@ -38,6 +38,12 @@
  This library is part of OsmoGGSN and implements the GTP protocol between
  SGSN (Serving GPRS support node) and GGSN.
 
+Package: gtp-echo-responder
+Architecture: any
+Depends: ${shlibs:Depends},
+         ${misc:Depends}
+Description: Small program answering GTP ECHO Request with GTP ECHO Response
+
 Package: libgtp-dev
 Architecture: any
 Multi-Arch: same
@@ -63,6 +69,15 @@
  operators as the interface between the Internet and the rest of the
  mobile network infrastructure.
 
+Package: gtp-echo-responder-dbg
+Section: debug
+Architecture: any
+Priority: extra
+Depends: ${shlibs:Depends}, ${misc:Depends}, gtp-echo-responder (= ${binary:Version})
+Multi-Arch: same
+Description: Debug symbols for gtp-echo-responder
+ Small program answering GTP ECHO Request with GTP ECHO Response.
+
 Package: libgtp-dbg
 Section: debug
 Architecture: any
diff --git a/debian/copyright b/debian/copyright
index e2a4b2d..034c84d 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -16,6 +16,11 @@
 Copyright: 1987-2001 Free Software Foundation, Inc.
 License: LGPL-2.1+
 
+Files: utils/gtp_echo_responder.c
+       utils/gtp_echo_responder_test.py
+Copyright: 2021 sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+License: MIT
+
 Files: debian/*
 Copyright: 2010-2017  Harald Welte <laforge at gnumonks.org>
            2016       Ruben Undheim <ruben.undheim at gmail.com>
diff --git a/debian/gtp-echo-responder.install b/debian/gtp-echo-responder.install
new file mode 100644
index 0000000..1d6d96f
--- /dev/null
+++ b/debian/gtp-echo-responder.install
@@ -0,0 +1 @@
+/usr/bin/gtp-echo-responder
diff --git a/utils/Makefile.am b/utils/Makefile.am
new file mode 100644
index 0000000..7ba0ff4
--- /dev/null
+++ b/utils/Makefile.am
@@ -0,0 +1,3 @@
+bin_PROGRAMS = gtp-echo-responder
+
+gtp_echo_responder_SOURCES = gtp_echo_responder.c
diff --git a/utils/gtp_echo_responder.c b/utils/gtp_echo_responder.c
new file mode 100644
index 0000000..e077c81
--- /dev/null
+++ b/utils/gtp_echo_responder.c
@@ -0,0 +1,470 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at sysmocom.de>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* For more info see:
+ * 3GPP TS 29.060 (GTPv1 and GTPv0)
+ * 3GPP TS 29.274 (GTPv2C)
+ */
+
+#include "../config.h"
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+
+#define GTP1C_PORT	2123
+#define GTP_MSGTYPE_ECHO_REQ	1
+#define GTP_MSGTYPE_ECHO_RSP	2
+#define GTP1C_IE_RECOVERY 14
+#define GTP2C_IE_RECOVERY 3
+#define GTP2C_IE_NODE_FEATURES 152
+
+struct gtp1_hdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
+#else
+	uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
+#endif
+	uint8_t type;
+	uint16_t length;
+	uint32_t tei;
+	uint16_t seq;
+	uint8_t npdu;
+	uint8_t next;
+} __attribute__((packed));
+
+struct gtp2_hdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	uint8_t reserved:3, t:1, p:1, version:3;
+#else
+	uint8_t version:3, p:1, t:1, reserved:1;
+#endif
+	uint8_t type;
+	uint16_t length;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	uint32_t reserved2:8, seq:24;
+#else
+	uint8_t seq:24, reserved2:1;
+#endif
+} __attribute__((packed));
+
+struct gtp_echo_resp_state {
+	struct {
+		char laddr[INET6_ADDRSTRLEN];
+		uint8_t recovery_ctr;
+		uint8_t node_features;
+	} cfg;
+	struct sockaddr_storage laddr_gtpc;
+	int fd_gtpc;
+};
+
+struct gtp_echo_resp_state *g_st;
+
+static void print_usage(void)
+{
+	printf("Usage: gtp-echo-responder [-h] [-V] [-l listen_addr]\n");
+}
+
+static void print_help(void)
+{
+	printf("  Some useful help...\n"
+	       "  -h --help		This help text\n"
+	       "  -V --version		Print the version of gtp-echo-responder\n"
+	       "  -l --listen-addr	Listend address for GTPCv1 and GTPCv2\n"
+	       "  -R --recovery-counter GTP Recovery Counter to transmit in GTP Echo Response message\n"
+	       "  -n --node-features	GTPCv2 Node Features bitmask to transmit in GTP Echo Response message\n"
+	       );
+}
+
+static void print_version(void)
+{
+	printf("gtp-echo-responder version %s\n", PACKAGE_VERSION);
+}
+
+static uint8_t parse_node_features_mask(const char *arg)
+{
+	unsigned long res;
+	char *end;
+	errno = 0;
+
+	res = strtoul(arg, &end, 0);
+	if ((errno == ERANGE && res == ULONG_MAX) || (errno && !res) ||
+	    arg == end) {
+		fprintf(stderr, "Failed parsing Node Features bitmask: '%s'\n", arg);
+		exit(1);
+	}
+	if (res > 0xff) {
+		fprintf(stderr, "Failed parsing Node Features bitmask: '%s' > 0xFF\n", arg);
+		exit(1);
+	}
+	return (uint8_t)res;
+}
+static void handle_options(int argc, char **argv)
+{
+	while (1) {
+		int option_index = 0, c;
+		static struct option long_options[] = {
+			{ "help", 0, 0, 'h' },
+			{ "version", 0, 0, 'V' },
+			{ "listen-addr", 1, 0, 'l'},
+			{ "recovery-counter", 1, 0, 'R'},
+			{ "node-features", 1, 0, 'N'},
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "hVl:R:N:", long_options, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_usage();
+			print_help();
+			exit(0);
+		case 'V':
+			print_version();
+			exit(0);
+			break;
+		case 'l':
+			strncpy(&g_st->cfg.laddr[0], optarg, sizeof(g_st->cfg.laddr));
+			g_st->cfg.laddr[sizeof(g_st->cfg.laddr) - 1] = '\0';
+			break;
+		case 'R':
+			g_st->cfg.recovery_ctr = (uint8_t)atoi(optarg);
+			break;
+		case 'N':
+			g_st->cfg.node_features = parse_node_features_mask(optarg);
+			break;
+		}
+	}
+}
+
+static int init_socket(void)
+{
+	struct in_addr addr;
+	struct in6_addr addr6;
+	struct sockaddr_in *saddr;
+	struct sockaddr_in6 *saddr6;
+	int family;
+
+	if (inet_pton(AF_INET6, g_st->cfg.laddr, &addr6) == 1) {
+		family = AF_INET6;
+		saddr6 = (struct sockaddr_in6 *)&g_st->laddr_gtpc;
+		saddr6->sin6_family = family;
+		saddr6->sin6_port = htons(GTP1C_PORT);
+		memcpy(&saddr6->sin6_addr, &addr6, sizeof(addr6));
+	} else if (inet_pton(AF_INET, g_st->cfg.laddr, &addr) == 1) {
+		family = AF_INET;
+		saddr = (struct sockaddr_in *)&g_st->laddr_gtpc;
+		saddr->sin_family = family;
+		saddr->sin_port = htons(GTP1C_PORT);
+		memcpy(&saddr->sin_addr, &addr, sizeof(addr));
+	} else {
+		fprintf(stderr, "Failed parsing address %s\n", g_st->cfg.laddr);
+		return -1;
+	}
+
+	if ((g_st->fd_gtpc = socket(family, SOCK_DGRAM, 0)) < 0) {
+		fprintf(stderr, "socket() failed: %s\n", strerror(errno));
+		return -2;
+	}
+
+	if (bind(g_st->fd_gtpc, (struct sockaddr *)&g_st->laddr_gtpc, sizeof(g_st->laddr_gtpc)) < 0) {
+		fprintf(stderr, "bind() failed: %s\n", strerror(errno));
+		return -3;
+	}
+
+	return 0;
+}
+
+static const char *sockaddr2str(const struct sockaddr *saddr)
+{
+	static char _rem_addr_str[INET6_ADDRSTRLEN];
+	struct sockaddr_in *saddr4;
+	struct sockaddr_in6 *saddr6;
+
+	switch (saddr->sa_family) {
+	case AF_INET6:
+		saddr6 = (struct sockaddr_in6 *)saddr;
+		if (!inet_ntop(saddr6->sin6_family, &saddr6->sin6_addr, _rem_addr_str, sizeof(_rem_addr_str)))
+			strcpy(_rem_addr_str, "unknown");
+		return _rem_addr_str;
+	case AF_INET:
+		saddr4 = (struct sockaddr_in *)saddr;
+		if (!inet_ntop(saddr4->sin_family, &saddr4->sin_addr, _rem_addr_str, sizeof(_rem_addr_str)))
+			strcpy(_rem_addr_str, "unknown");
+		return _rem_addr_str;
+	default:
+		strcpy(_rem_addr_str, "unknown-family");
+		return _rem_addr_str;
+	}
+}
+
+static int write_cb(int fd, const uint8_t *buf, size_t buf_len, const struct sockaddr *rem_saddr)
+{
+	ssize_t rc;
+
+	rc = sendto(fd, buf, buf_len, 0, rem_saddr, sizeof(struct sockaddr_storage));
+	if (rc < 0) {
+		fprintf(stderr, "sendto() failed: %s\n", strerror(errno));
+		return -1;
+	}
+	if (rc != buf_len) {
+		fprintf(stderr, "sendto() short write: %zd vs exp %zu\n", rc, buf_len);
+		return -1;
+	}
+	return 0;
+}
+
+static int gen_gtpc1_echo_rsp(uint8_t *buf, struct gtp1_hdr *echo_req)
+{
+	int offset = 0;
+	struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
+	unsigned exp_hdr_len = (echo_req->s || echo_req->pn || echo_req->e) ? 12 : 8;
+
+	memcpy(echo_rsp, echo_req, exp_hdr_len);
+	echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
+	offset = exp_hdr_len;
+	buf[offset++] = GTP1C_IE_RECOVERY;
+	buf[offset++] = g_st->cfg.recovery_ctr;
+
+	/* Update Length */
+	echo_rsp->length = htons(offset - 8);
+	return offset;
+}
+
+static int gen_gtpc2_echo_rsp(uint8_t *buf, struct gtp2_hdr *echo_req)
+{
+	int offset = 0;
+	struct gtp1_hdr *echo_rsp = (struct gtp1_hdr *)buf;
+	unsigned exp_hdr_len = 8;
+
+	memcpy(echo_rsp, echo_req, exp_hdr_len);
+	echo_rsp->type = GTP_MSGTYPE_ECHO_RSP;
+	offset = exp_hdr_len;
+
+	/* 3GPP TS 29.274 sec 8.5 Recovery (Restart Counter) */
+	buf[offset++] = GTP2C_IE_RECOVERY;
+	buf[offset++] = 0; /* IE Length (high) */
+	buf[offset++] = 1; /* IE Length (low) */
+	buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
+	buf[offset++] = g_st->cfg.recovery_ctr;
+
+	/* 3GPP TS 29.274 sec 8.83 Node Features */
+	if (g_st->cfg.node_features > 0) {
+		buf[offset++] = GTP2C_IE_NODE_FEATURES;
+		buf[offset++] = 0; /* IE Length (high) */
+		buf[offset++] = 1; /* IE Length (low) */
+		buf[offset++] = 0; /* Spare=0 | Instance=0 (Table 7.1.1-1) */
+		buf[offset++] = g_st->cfg.node_features;
+	}
+
+	/* Update Length */
+	echo_rsp->length = htons(offset - 4);
+	return offset;
+}
+
+static int rx_gtpc1_echo_req(struct gtp1_hdr *echo_req, unsigned buf_len, const struct sockaddr *rem_saddr)
+{
+	int rc;
+	const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
+	uint8_t *tx_buf = alloca(tx_buf_len);
+
+	printf("Rx GTPCv1_ECHO_REQ from %s, Tx GTPCv1_ECHO_RSP\n", sockaddr2str(rem_saddr));
+
+	memset(tx_buf, 0, tx_buf_len);
+	rc = gen_gtpc1_echo_rsp(tx_buf, echo_req);
+	return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
+}
+
+static int rx_gtpc1(struct gtp1_hdr *hdr, unsigned buf_len, const struct sockaddr *rem_saddr)
+{
+	unsigned exp_hdr_len = (hdr->s || hdr->pn || hdr->e) ? 12 : 8;
+	unsigned pdu_len;
+
+	if (buf_len < exp_hdr_len) {
+		fprintf(stderr, "GTPCv1 packet size smaller than header! %u < exp %u\n", buf_len, exp_hdr_len);
+		return -1;
+	}
+
+	pdu_len = ntohs(hdr->length);
+	if (buf_len < 8 + pdu_len) {
+		fprintf(stderr, "GTPCv1 packet size smaller than announced! %u < exp %u\n", buf_len, 8 + pdu_len);
+		return -1;
+	}
+
+	if (hdr->pt != 1) {
+		fprintf(stderr, "GTPCv1 Protocol Type GTP' not supported!\n");
+		return -1;
+	}
+
+	switch (hdr->type) {
+	case GTP_MSGTYPE_ECHO_REQ:
+		return rx_gtpc1_echo_req(hdr, buf_len, rem_saddr);
+	default:
+		fprintf(stderr, "Silently ignoring unexpected packet of type %u\n", hdr->type);
+		return 0;
+	}
+}
+
+static int rx_gtpc2_echo_req(struct gtp2_hdr *echo_req, unsigned buf_len, const struct sockaddr *rem_saddr)
+{
+	int rc;
+	const size_t tx_buf_len = buf_len + 128; /* Leave some extra room */
+	uint8_t *tx_buf = alloca(tx_buf_len);
+
+	if (echo_req->t) {
+		fprintf(stderr, "GTPCv2 ECHO message should contain T=0!\n");
+		return -1;
+	}
+
+	printf("Rx GTPCv2_ECHO_REQ from %s, Tx GTPCv2_ECHO_RSP\n", sockaddr2str(rem_saddr));
+
+	memset(tx_buf, 0, tx_buf_len);
+	rc = gen_gtpc2_echo_rsp(tx_buf, echo_req);
+	return write_cb(g_st->fd_gtpc, tx_buf, rc, rem_saddr);
+}
+
+static int rx_gtpc2(struct gtp2_hdr *hdr, unsigned buf_len, const struct sockaddr *rem_saddr)
+{
+	unsigned exp_hdr_len = hdr->t ? 12 : 8;
+	unsigned pdu_len;
+
+	if (hdr->p) {
+		fprintf(stderr, "GTPCv2 piggybacked message not supported!\n");
+		return -1;
+	}
+
+	if (buf_len < exp_hdr_len) {
+		fprintf(stderr, "GTPCv2 packet size smaller than header! %u < exp %u\n", buf_len, exp_hdr_len);
+		return -1;
+	}
+
+	pdu_len = ntohs(hdr->length);
+	/* 3GPP TS 29.274 sec 5.5.1: "Octets 3 to 4 represent the Message Length
+	 * field. This field shall indicate the length of the message in octets
+	 * excluding the mandatory part of the GTP-C header (the first 4
+	 * octets). The TEID (if present) and the Sequence  Number shall be
+	 * included in the length count" */
+	if (buf_len < 4 + pdu_len) {
+		fprintf(stderr, "GTPCv2 packet size smaller than announced! %u < exp %u\n", buf_len, 4 + pdu_len);
+		return -1;
+	}
+
+	switch (hdr->type) {
+	case GTP_MSGTYPE_ECHO_REQ:
+		return rx_gtpc2_echo_req(hdr, buf_len, rem_saddr);
+	default:
+		fprintf(stderr, "Silently ignoring unexpected packet of type %u\n", hdr->type);
+		return 0;
+	}
+}
+
+static int read_cb(int fd)
+{
+	ssize_t sz;
+	uint8_t buf[4096];
+	struct sockaddr_storage rem_saddr;
+	socklen_t rem_saddr_len = sizeof(rem_saddr);
+	struct gtp1_hdr *hdr1;
+
+	if ((sz = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&rem_saddr, &rem_saddr_len)) < 0) {
+		fprintf(stderr, "recvfrom() failed: %s\n", strerror(errno));
+		return -1;
+	}
+	if (sz == 0) {
+		fprintf(stderr, "recvfrom() read zero bytes!\n");
+		return -1;
+	}
+
+	hdr1 = (struct gtp1_hdr *)&buf[0];
+	switch (hdr1->version) {
+	case 1:
+		return rx_gtpc1(hdr1, sz, (const struct sockaddr *)&rem_saddr);
+	case 2:
+		return rx_gtpc2((struct gtp2_hdr *)&buf[0], sz, (const struct sockaddr *)&rem_saddr);
+	default:
+		fprintf(stderr, "Rx GTPv%u: not supported (flags=0x%x)\n", hdr1->version, buf[0]);
+		return -1;
+	}
+}
+
+static int loop(void)
+{
+	int rc;
+	fd_set rfds;
+	int nfds;
+
+	while (true) {
+		FD_ZERO(&rfds);
+		FD_SET(g_st->fd_gtpc, &rfds);
+		nfds = g_st->fd_gtpc + 1;
+		rc = select(nfds, &rfds, NULL, NULL, NULL);
+		if (rc == 0)
+			continue;
+		if (rc < 0) {
+			fprintf(stderr, "select() failed: %s\n", strerror(errno));
+			return -1;
+		}
+
+		if (FD_ISSET(g_st->fd_gtpc, &rfds))
+			read_cb(g_st->fd_gtpc);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	g_st = calloc(1, sizeof(struct gtp_echo_resp_state));
+
+	strcpy(g_st->cfg.laddr, "::");
+
+	handle_options(argc, argv);
+
+	printf("Listening on: %s\n", g_st->cfg.laddr);
+
+	if (init_socket() < 0)
+		exit(1);
+
+	printf("Socket bound successfully, listening for requests...\n");
+
+	if (loop() < 0)
+		exit(1);
+
+	return 0;
+}
diff --git a/utils/gtp_echo_responder_test.py b/utils/gtp_echo_responder_test.py
new file mode 100755
index 0000000..632d23b
--- /dev/null
+++ b/utils/gtp_echo_responder_test.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# MIT License
+#
+# Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info at sysmocom.de>
+# Author: Pau Espin Pedrol <pespin at sysmocom.de>
+#
+# SPDX-License-Identifier: MIT
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+import socket
+import argparse
+import struct
+from ipaddress import ip_address, IPv4Address
+
+GTP1C_PORT = 2123
+BUF_SIZE = 4096
+
+GTP_HDRv1_FLAG_PN = (1<<0)
+GTP_HDRv1_FLAG_S = (1<<1)
+GTP_HDRv1_FLAG_E = (1<<2)
+GTP_HDRv1_PT_GTP = (1<<4)
+GTP_HDRv1_VER_GTP1 = (1<<5)
+
+GTP_HDRv2_FLAG_T = (1<<3)
+GTP_HDRv2_FLAG_P = (1<<4)
+GTP_HDRv2_VER_GTP2 = (2<<5)
+
+def gen_gtpc_v1_hdr(flags, type, length, tei, seq=0, npdu=0, next=0):
+    spare = 0
+    if (flags & (GTP_HDRv1_FLAG_PN|GTP_HDRv1_FLAG_S|GTP_HDRv1_FLAG_E)):
+        #long format
+        length += 4
+        d = struct.pack('!BBHIHBB', flags, type, length, tei, seq, npdu, next)
+    else:
+        #short format
+        d = struct.pack('!BBHI', flags, type, length, tei)
+    return d
+
+def gen_gtpc_v2_hdr(flags, type, length, tei=0, seq=0):
+    spare = 0
+    if (flags & (GTP_HDRv2_FLAG_T)):
+        #long format, with TEI
+        length += 4 + 4
+        d = struct.pack('!BBHIHBB', flags, type, length, tei, seq >> 8, seq & 0xff, spare)
+    else:
+        #short format
+        length += 4
+        d = struct.pack('!BBHHBB', flags, type, length, seq >> 8, seq & 0xff, spare)
+    return d
+
+def gen_gtpc_v1_echo_req(tei=0, append_flags=0, seq=0, npdu=0, next=0):
+    return gen_gtpc_v1_hdr(GTP_HDRv1_VER_GTP1 | GTP_HDRv1_PT_GTP | append_flags, 1, 0, tei, seq, npdu, next)
+
+def gen_gtpc_v2_echo_req(append_flags=0, seq=0, recovery=0, node_features=-1):
+    length = 0
+    payload = b''
+    if (recovery > 0):
+        recovery_ie = struct.pack('!BHBB', 3, 1, 0, recovery)
+        payload += recovery_ie
+        length += len(recovery_ie)
+    if (node_features > 0):
+        node_features_ie = struct.pack('!BHBB', 152, 1, 0, node_features)
+        payload += node_features_ie
+        length += len(node_features_ie)
+    return gen_gtpc_v2_hdr(GTP_HDRv2_VER_GTP2 | append_flags, 1, length, 0, seq) + payload
+
+def tx_rx(sk, rem_addr, tx_buf, exp_rx = True):
+    print('Tx ECHO_REQ to %r: %r' % (repr(rem_addr), repr(tx_buf)))
+    sk.sendto(tx_buf, rem_addr)
+    if exp_rx:
+        rx_buf = sk.recvfrom(BUF_SIZE)
+        msg = "Message from Server {}".format(rx_buf)
+        print(msg)
+
+if __name__ == '__main__':
+    p = argparse.ArgumentParser(description='Tester for gtp-echo-recorder.')
+    p.add_argument('-l', '--local-address', default='127.0.0.2', help="Local GTP address")
+    p.add_argument('-r', '--remote-address', default='127.0.0.1', help="Remote GTP address")
+    args = p.parse_args()
+
+    print('Binding socket on %r...' % repr((args.local_address, GTP1C_PORT)))
+    family = socket.AF_INET if type(ip_address(args.local_address)) is IPv4Address else socket.AF_INET6
+    sk = socket.socket(family=family, type=socket.SOCK_DGRAM)
+    sk.bind((args.local_address, GTP1C_PORT));
+
+    rem_addr = (args.remote_address, GTP1C_PORT)
+
+    tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req())
+    tx_rx(sk, rem_addr, gen_gtpc_v1_echo_req(1, GTP_HDRv1_FLAG_S, seq=67))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=300, recovery=-1, node_features=-1))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=99, node_features=-1))
+    tx_rx(sk, rem_addr, gen_gtpc_v2_echo_req(0, seq=20, recovery=100, node_features=0xbb))

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ggsn/+/25652
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: Ibdd6d8f6920571db0c60cf8b3b25d541b15ad3f1
Gerrit-Change-Number: 25652
Gerrit-PatchSet: 7
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: osmith <osmith at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20211008/4f204bda/attachment.htm>


More information about the gerrit-log mailing list