Change in osmo-ggsn[master]: WIP: 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/.

pespin gerrit-no-reply at lists.osmocom.org
Thu Sep 30 14:13:40 UTC 2021


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/25652 )


Change subject: WIP: gtp-echo-responder
......................................................................

WIP: gtp-echo-responder

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



  git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/52/25652/1

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/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..66479c7
--- /dev/null
+++ b/utils/gtp_echo_responder.c
@@ -0,0 +1,338 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info 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 <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 GTP_IE_RECOVERY 14
+
+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 gtp_echo_resp_state {
+	struct {
+		char laddr[INET6_ADDRSTRLEN];
+		uint8_t recovery_ctr;
+	} cfg;
+	struct sockaddr_storage laddr_gtpc1;
+	int fd_gtpc1;
+};
+
+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"
+		);
+}
+
+static void print_version(void)
+{
+	printf("gtp-echo-responder version %s\n", PACKAGE_VERSION);
+}
+
+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'},
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "hdVl:R:", 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);
+		}
+	}
+}
+
+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_gtpc1;
+		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_gtpc1;
+		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_gtpc1 = socket(family, SOCK_DGRAM, 0)) < 0) {
+		fprintf(stderr, "socket() failed: %s\n", strerror(errno));
+		return -2;
+	}
+
+	if (bind(g_st->fd_gtpc1, (struct sockaddr *)&g_st->laddr_gtpc1, sizeof(g_st->laddr_gtpc1)) < 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)))
+			goto err;
+		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)))
+			goto err;
+		return _rem_addr_str;
+	default:
+		/* fall through */
+	}
+
+err:
+	strcpy(_rem_addr_str, "unknwown");
+	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: %u vs exp %u\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++] = GTP_IE_RECOVERY;
+	buf[offset++] = g_st->cfg.recovery_ctr;
+
+	/* Update Length */
+	echo_rsp->length = htons(offset - 8);
+	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_gtpc1, 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 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);
+	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_gtpc1, &rfds);
+		nfds = g_st->fd_gtpc1 + 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_gtpc1, &rfds)) {
+			read_cb(g_st->fd_gtpc1);
+		}
+	}
+}
+
+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 successfuly, 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..e90b129
--- /dev/null
+++ b/utils/gtp_echo_responder_test.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# MIT License
+#
+# Copyright (c) 2021 by sysmocom - s.f.m.c. GmbH <info 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)
+
+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_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 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))

-- 
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: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210930/8b2947d3/attachment.htm>


More information about the gerrit-log mailing list