neels has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-upf/+/28312 )


Change subject: implement GTPv1-U ECHO response
......................................................................

implement GTPv1-U ECHO response

Accept data on the GTPv1-U socket and respond to GTPv1-U ECHO REQUEST
messages.

We should keep a deterministic recovery counter that increases with
every restart. As a quick and dirty way just use the current time at
startup for now, until osmo-upf reaches production maturity.

Related: OS#5599
Change-Id: I135370a7723e2c667ec681f50c21107cde63ea5b
---
M include/osmocom/upf/Makefile.am
M include/osmocom/upf/upf.h
A include/osmocom/upf/upf_gtpu_echo.h
M src/osmo-upf/Makefile.am
M src/osmo-upf/upf.c
M src/osmo-upf/upf_gtp.c
A src/osmo-upf/upf_gtpu_echo.c
7 files changed, 169 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-upf refs/changes/12/28312/1

diff --git a/include/osmocom/upf/Makefile.am b/include/osmocom/upf/Makefile.am
index 333132c..127e03e 100644
--- a/include/osmocom/upf/Makefile.am
+++ b/include/osmocom/upf/Makefile.am
@@ -4,6 +4,7 @@
        up_session.h \
        upf.h \
        upf_gtp.h \
+       upf_gtpu_echo.h \
        upf_nft.h \
        up_gtp_action.h \
        $(NULL)
diff --git a/include/osmocom/upf/upf.h b/include/osmocom/upf/upf.h
index 49217c6..6f4d01c 100644
--- a/include/osmocom/upf/upf.h
+++ b/include/osmocom/upf/upf.h
@@ -84,6 +84,8 @@

                struct mnl_socket *nl;
                int32_t genl_id;
+
+               uint8_t recovery_count;
        } gtp;

        /* Tunnel forwarding via linux netfilter */
diff --git a/include/osmocom/upf/upf_gtpu_echo.h 
b/include/osmocom/upf/upf_gtpu_echo.h
new file mode 100644
index 0000000..2575424
--- /dev/null
+++ b/include/osmocom/upf/upf_gtpu_echo.h
@@ -0,0 +1,4 @@
+/* GTP-U ECHO implementation for osmo-upf */
+#pragma once
+
+int upf_gtpu_echo_setup(struct upf_gtp_dev *dev);
diff --git a/src/osmo-upf/Makefile.am b/src/osmo-upf/Makefile.am
index 1265051..1d43ee8 100644
--- a/src/osmo-upf/Makefile.am
+++ b/src/osmo-upf/Makefile.am
@@ -37,6 +37,7 @@
        up_session.c \
        upf.c \
        upf_gtp.c \
+       upf_gtpu_echo.c \
        upf_nft.c \
        upf_vty.c \
        $(NULL)
diff --git a/src/osmo-upf/upf.c b/src/osmo-upf/upf.c
index 9677147..2faa558 100644
--- a/src/osmo-upf/upf.c
+++ b/src/osmo-upf/upf.c
@@ -53,6 +53,10 @@
                .nft = {
                        .priority = -300,
                },
+               .gtp = {
+                       /* TODO: recovery count state file; use lower byte of 
current time, poor person's random. */
+                       .recovery_count = time(NULL),
+               },
        };

        INIT_LLIST_HEAD(&g_upf->gtp.vty_cfg.devs);
diff --git a/src/osmo-upf/upf_gtp.c b/src/osmo-upf/upf_gtp.c
index 84d74d1..4524795 100644
--- a/src/osmo-upf/upf_gtp.c
+++ b/src/osmo-upf/upf_gtp.c
@@ -36,6 +36,7 @@

 #include <osmocom/upf/upf.h>
 #include <osmocom/upf/upf_gtp.h>
+#include <osmocom/upf/upf_gtpu_echo.h>

 #define LOG_GTP_TUN(TUN, LEVEL, FMT, ARGS...) \
        LOGP(DGTP, LEVEL, "%s: " FMT, upf_gtp_tun_to_str_c(OTC_SELECT, (TUN)), 
##ARGS)
@@ -221,6 +222,8 @@
                return -EIO;
        }

+       upf_gtpu_echo_setup(dev);
+
        return 0;
 }

diff --git a/src/osmo-upf/upf_gtpu_echo.c b/src/osmo-upf/upf_gtpu_echo.c
new file mode 100644
index 0000000..24452b7
--- /dev/null
+++ b/src/osmo-upf/upf_gtpu_echo.c
@@ -0,0 +1,154 @@
+/* GTP-U ECHO implementation for osmo-upf */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/endian.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/upf/upf.h>
+#include <osmocom/upf/upf_gtp.h>
+
+#define GTP1U_PORT     2152
+
+enum gtp1u_msgt {
+       GTP1U_MSGTYPE_ECHO_REQ = 1,
+       GTP1U_MSGTYPE_ECHO_RSP = 2,
+};
+
+enum gtp1u_iei {
+       GTP1U_IEI_RECOVERY = 14,
+};
+
+/* 3GPP TS 29.281 */
+struct gtp1u_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+       uint8_t pn:1, /*< N-PDU Number flag */
+               s:1, /*< Sequence number flag */
+               e:1, /*< Extension header flag */
+               spare:1,
+               pt:1, /*< Protocol Type: GTP=1, GTP'=0 */
+               version:3; /*< Version: 1 */
+#elif OSMO_IS_BIG_ENDIAN
+       uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
+#endif
+       uint8_t msg_type;
+       uint16_t length;
+       uint32_t tei; /*< 05 - 08 Tunnel Endpoint ID */
+       union {
+               uint8_t data1[0];
+               struct {
+                       uint16_t seq_nr;
+                       uint8_t n_pdu_nr;
+                       uint8_t next_ext_type;
+               } ext;
+       };
+       uint8_t data2[0];
+} __attribute__((packed));
+
+static int rx_echo_req(struct upf_gtp_dev *dev, const struct osmo_sockaddr 
*remote, const struct gtp1u_hdr *rx_h)
+{
+       struct msgb *msg;
+       struct gtp1u_hdr *tx_h;
+       int rc;
+
+       if (!rx_h->s) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTPv1-U ECHO REQ without 
sequence nr\n");
+               return -1;
+       }
+
+       msg = msgb_alloc_headroom(1024, 128, "GTP-echo-resp");
+       tx_h = (void*)msgb_put(msg, sizeof(*tx_h));
+
+       *tx_h = (struct gtp1u_hdr){
+               /* 3GPP TS 29.281 5.1 defines that the ECHO REQ & RESP shall 
contain a sequence nr */
+               .s = 1,
+               .pt = 1,
+               .version = 1,
+               .msg_type = GTP1U_MSGTYPE_ECHO_RSP,
+               .ext = {
+                       .seq_nr = rx_h->ext.seq_nr,
+               },
+       };
+
+       OSMO_ASSERT(msg->tail == tx_h->data2);
+
+       /* ECHO RESPONSE shall contain a recovery counter */
+       msgb_put_u8(msg, GTP1U_IEI_RECOVERY);
+       msgb_put_u8(msg, g_upf->gtp.recovery_count);
+
+       osmo_store16be(msg->tail - tx_h->data1, &tx_h->length);
+
+       rc = sendto(dev->gtpv1.ofd.fd, msgb_data(msg), msgb_length(msg), 0, 
&remote->u.sa, sizeof(*remote));
+       if (rc < 0) {
+               int err = errno;
+               LOG_GTP_DEV(dev, LOGL_ERROR, "GTP1-U sendto(len=%d, to=%s): 
%s\n", msgb_length(msg),
+                           osmo_sockaddr_to_str(remote), strerror(err));
+       }
+       return rc;
+}
+
+int upf_gtpu_echo_read_cb(struct osmo_fd *ofd, unsigned int what)
+{
+       struct upf_gtp_dev *dev = ofd->data;
+
+       ssize_t sz;
+       uint8_t buf[4096];
+       struct osmo_sockaddr remote;
+       socklen_t remote_len = sizeof(remote);
+       const struct gtp1u_hdr *h;
+       uint16_t h_length;
+
+       if ((sz = recvfrom(dev->gtpv1.ofd.fd, buf, sizeof(buf), 0, 
&remote.u.sa, &remote_len)) < 0) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "recvfrom() failed: %s\n", 
strerror(errno));
+               return -1;
+       }
+       if (sz == 0) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "recvfrom() yields zero bytes\n");
+               return -1;
+       }
+
+       /* A GTPv1-U header of size 8 is valid, but this code expects to handle 
only ECHO REQUEST messages. These are
+        * required to have a sequence number, hence this check here 
consciously uses the full sizeof(*h) == 12. */
+       if (sz < sizeof(*h)) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP packet smaller than the 
GTPv1-U header + sequence nr: %zd < %zu\n",
+                           sz, sizeof(*h));
+               return -1;
+       }
+
+       h = (const struct gtp1u_hdr *)buf;
+       if (h->version != 1) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP v%u: only GTP version 1 
supported\n", h->version);
+               return -1;
+       }
+
+       h_length = osmo_load16be(&h->length);
+       if (offsetof(struct gtp1u_hdr, data1) + h_length > sz) {
+               LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTP: header + h.length = %zu > 
received bytes = %zd\n",
+                           offsetof(struct gtp1u_hdr, data1) + h_length, sz);
+               return -1;
+       }
+
+       switch (h->msg_type) {
+       case GTP1U_MSGTYPE_ECHO_REQ:
+               return rx_echo_req(dev, &remote, h);
+       default:
+               LOG_GTP_DEV(dev, LOGL_ERROR, "rx: GTPv1-U message type %u not 
supported\n", h->msg_type);
+               return -1;
+       }
+
+       return 0;
+}
+
+int upf_gtpu_echo_setup(struct upf_gtp_dev *dev)
+{
+       if (dev->gtpv1.ofd.fd == -1) {
+               LOGP(DGTP, LOGL_ERROR, "Cannot setup GTP-U ECHO: GTP-v1 socket 
not initialized\n");
+               return -EINVAL;
+       }
+
+       dev->gtpv1.ofd.cb = upf_gtpu_echo_read_cb;
+       dev->gtpv1.ofd.data = dev;
+       return osmo_fd_register(&dev->gtpv1.ofd);
+}

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

Gerrit-Project: osmo-upf
Gerrit-Branch: master
Gerrit-Change-Id: I135370a7723e2c667ec681f50c21107cde63ea5b
Gerrit-Change-Number: 28312
Gerrit-PatchSet: 1
Gerrit-Owner: neels <[email protected]>
Gerrit-MessageType: newchange

Reply via email to