mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
synced 2025-11-02 13:03:35 +00:00
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
This commit is contained in:
@@ -4,6 +4,7 @@ noinst_HEADERS = \
|
||||
up_session.h \
|
||||
upf.h \
|
||||
upf_gtp.h \
|
||||
upf_gtpu_echo.h \
|
||||
upf_nft.h \
|
||||
up_gtp_action.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -87,6 +87,8 @@ struct g_upf {
|
||||
|
||||
struct mnl_socket *nl;
|
||||
int32_t genl_id;
|
||||
|
||||
uint8_t recovery_count;
|
||||
} gtp;
|
||||
|
||||
/* Tunnel forwarding via linux netfilter */
|
||||
|
||||
4
include/osmocom/upf/upf_gtpu_echo.h
Normal file
4
include/osmocom/upf/upf_gtpu_echo.h
Normal file
@@ -0,0 +1,4 @@
|
||||
/* GTP-U ECHO implementation for osmo-upf */
|
||||
#pragma once
|
||||
|
||||
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev);
|
||||
@@ -37,6 +37,7 @@ osmo_upf_SOURCES = \
|
||||
up_session.c \
|
||||
upf.c \
|
||||
upf_gtp.c \
|
||||
upf_gtpu_echo.c \
|
||||
upf_nft.c \
|
||||
upf_vty.c \
|
||||
$(NULL)
|
||||
|
||||
@@ -53,6 +53,10 @@ void g_upf_alloc(void *ctx)
|
||||
.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);
|
||||
|
||||
@@ -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)
|
||||
@@ -228,6 +229,8 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
upf_gtpu_echo_setup(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
161
src/osmo-upf/upf_gtpu_echo.c
Normal file
161
src/osmo-upf/upf_gtpu_echo.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* 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 tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr);
|
||||
|
||||
static int rx_echo_req(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, const struct gtp1u_hdr *rx_h)
|
||||
{
|
||||
if (!rx_h->s) {
|
||||
LOG_GTP_DEV(dev, LOGL_ERROR, "rx GTPv1-U ECHO REQ without sequence nr\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tx_echo_resp(dev, remote, rx_h->ext.seq_nr);
|
||||
}
|
||||
|
||||
static int tx_echo_resp(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gtp1u_hdr *tx_h;
|
||||
int rc;
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
Reference in New Issue
Block a user