<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-ggsn/+/25652">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved; Verified
  osmith: Looks good to me, but someone else must approve

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

<div style="display:none"> Gerrit-Project: osmo-ggsn </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ibdd6d8f6920571db0c60cf8b3b25d541b15ad3f1 </div>
<div style="display:none"> Gerrit-Change-Number: 25652 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>