osmith has submitted this change. ( https://gerrit.osmocom.org/c/osmo-upf/+/37830?usp=email )
Change subject: osmo-upf: add VTY 'gtp-echo' command ......................................................................
osmo-upf: add VTY 'gtp-echo' command
Allow sending GTPv1-U Echo Requests to GTP peers via new VTY command
gtp1u-echo send to (A.B.C.D|X:X::X:X) gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X) gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME
Related: OS#6541 Tweaked-by: Oliver Smith osmith@sysmocom.de Change-Id: I970dccd7a27b098eea9e660822e24e2c4b059fc6 --- M include/osmocom/upf/upf.h M include/osmocom/upf/upf_gtpu_echo.h M src/osmo-upf/upf_gtpu_echo.c M src/osmo-upf/upf_vty.c A tests/gtp-echo.vty 5 files changed, 184 insertions(+), 0 deletions(-)
Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved pespin: Looks good to me, but someone else must approve
diff --git a/include/osmocom/upf/upf.h b/include/osmocom/upf/upf.h index 145567b..d558600 100644 --- a/include/osmocom/upf/upf.h +++ b/include/osmocom/upf/upf.h @@ -118,6 +118,7 @@
struct { uint32_t next_local_teid_state; + uint16_t next_echo_seq_nr; } gtp;
struct llist_head netinst; diff --git a/include/osmocom/upf/upf_gtpu_echo.h b/include/osmocom/upf/upf_gtpu_echo.h index 2575424..2bb7c31 100644 --- a/include/osmocom/upf/upf_gtpu_echo.h +++ b/include/osmocom/upf/upf_gtpu_echo.h @@ -2,3 +2,4 @@ #pragma once
int upf_gtpu_echo_setup(struct upf_gtp_dev *dev); +int upf_gtpu_echo_req_tx(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr); diff --git a/src/osmo-upf/upf_gtpu_echo.c b/src/osmo-upf/upf_gtpu_echo.c index b234065..f4bbb76 100644 --- a/src/osmo-upf/upf_gtpu_echo.c +++ b/src/osmo-upf/upf_gtpu_echo.c @@ -122,6 +122,43 @@ return rc; }
+int upf_gtpu_echo_req_tx(struct upf_gtp_dev *dev, const struct osmo_sockaddr *remote, uint16_t seq_nr) +{ + struct gtp1u_hdr *tx_h; + int rc; + uint8_t msgbuf[sizeof(struct gtp1u_hdr) + 2]; + + tx_h = (void *)msgbuf; + *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_REQ, + .ext = { + .seq_nr = seq_nr, + }, + }; + + /* ECHO REQUEST shall contain a recovery counter */ + tx_h->data2[0] = GTP1U_IEI_RECOVERY; + tx_h->data2[1] = g_upf->tunend.recovery_count; + + osmo_store16be(sizeof(msgbuf) - offsetof(struct gtp1u_hdr, data1), &tx_h->length); + + rc = sendto(dev->gtpv1.ofd.fd, msgbuf, sizeof(msgbuf), 0, &remote->u.sa, sizeof(*remote)); + if (rc < 0) { + rc = -errno; + LOG_GTP_DEV(dev, LOGL_ERROR, "GTP1-U sendto(len=%zu, to=%s): %s\n", sizeof(msgbuf), + osmo_sockaddr_to_str(remote), strerror(-rc)); + } else { + rc = 0; + } + LOG_GTP_DEV(dev, LOGL_INFO, "<- %s: tx GTP1-U Echo Request: seq_nr=%u recovery_count=%u\n", + osmo_sockaddr_to_str(remote), seq_nr, g_upf->tunend.recovery_count); + return rc; +} + int upf_gtpu_echo_read_cb(struct osmo_fd *ofd, unsigned int what) { struct upf_gtp_dev *dev = ofd->data; diff --git a/src/osmo-upf/upf_vty.c b/src/osmo-upf/upf_vty.c index 44d14db..9c5f7f0 100644 --- a/src/osmo-upf/upf_vty.c +++ b/src/osmo-upf/upf_vty.c @@ -38,6 +38,7 @@ #include <osmocom/upf/up_session.h> #include <osmocom/upf/up_gtp_action.h> #include <osmocom/upf/netinst.h> +#include <osmocom/upf/upf_gtpu_echo.h>
enum upf_vty_node { PFCP_NODE = _LAST_OSMOVTY_NODE + 1, @@ -486,6 +487,105 @@ return CMD_SUCCESS; }
+/* variant: + * 0 "gtp1u-echo send to (A.B.C.D|X:X::X:X)" + * 1 "gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X)" + * 2 "gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME" + */ +static int _gtp_echo_tx(struct vty *vty, int variant, int argc, const char **argv) +{ + struct osmo_sockaddr_str addr; + struct osmo_sockaddr osa_remote; + struct osmo_sockaddr osa_local; + struct upf_gtp_dev *gtp_dev = NULL; + const char *remote_str = argv[0]; + const char *local_str = NULL; + if (argc > 1) + local_str = argv[1]; + + /* GTP can be received on port 2152 only, i.e. the remote port must be 2152. (The sending port is allowed to + * differ). */ + if (osmo_sockaddr_str_from_str(&addr, remote_str, 2152) + || osmo_sockaddr_str_to_osa(&addr, &osa_remote)) { + vty_out(vty, "%% Error: cannot send Echo: invalid IP address: %s%s", + osmo_quote_str(remote_str, -1), VTY_NEWLINE); + return CMD_WARNING; + } + + switch (variant) { + case 0: + gtp_dev = llist_first_entry_or_null(&g_upf->tunend.devs, struct upf_gtp_dev, entry); + if (!gtp_dev) { + vty_out(vty, "%% Error: cannot send Echo: there is no GTP device%s", + VTY_NEWLINE); + return CMD_WARNING; + } + break; + case 1: + if (osmo_sockaddr_str_from_str(&addr, local_str, 2152) + || osmo_sockaddr_str_to_osa(&addr, &osa_local)) { + vty_out(vty, "%% Error: cannot send Echo: invalid IP address: %s%s", + osmo_quote_str(local_str, -1), VTY_NEWLINE); + return CMD_WARNING; + } + gtp_dev = upf_gtp_dev_find_by_local_addr(&osa_local); + if (!gtp_dev) { + vty_out(vty, "%% Error: cannot send Echo: this does not seem to be a locally bound GTP address: %s%s", + osmo_sockaddr_to_str_c(OTC_SELECT, &osa_local), VTY_NEWLINE); + return CMD_WARNING; + } + break; + case 2: + gtp_dev = upf_gtp_dev_find_by_name(local_str); + if (!gtp_dev) { + vty_out(vty, "%% Error: cannot send Echo: there is no GTP device by the name of '%s'%s", + local_str, VTY_NEWLINE); + return CMD_WARNING; + } + break; + } + OSMO_ASSERT(gtp_dev); + + if (upf_gtpu_echo_req_tx(gtp_dev, &osa_remote, g_upf->gtp.next_echo_seq_nr++)) { + vty_out(vty, "%% Error: Failed to transmit Echo Request (see DGTP logging)%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty_out(vty, "%s -> %s tx Echo Request; for responses, see DGTP logging level INFO%s", + gtp_dev->name, osmo_sockaddr_to_str_c(OTC_SELECT, &osa_remote), VTY_NEWLINE); + return CMD_SUCCESS; +} + +#define IP46_STR "IPv4 address\nIPv6 address\n" +#define GTP_ECHO_TX_STR \ + "GTP1-U Echo probing\n" \ + "Send a GTP1-U Echo Request to a remote peer\n" \ + "Send to remote peer's GTP address\n" IP46_STR + +DEFUN(gtp_echo_tx, gtp_echo_tx_cmd, + "gtp1u-echo send to " VTY_IPV46_CMD, + GTP_ECHO_TX_STR) +{ + return _gtp_echo_tx(vty, 0, argc, argv); +} + +DEFUN(gtp_echo_tx_local_ip, gtp_echo_tx_local_ip_cmd, + "gtp1u-echo send to " VTY_IPV46_CMD " local-ip " VTY_IPV46_CMD, + GTP_ECHO_TX_STR + "Send from local GTP device, chosen by IP address\n" + IP46_STR) +{ + return _gtp_echo_tx(vty, 1, argc, argv); +} + +DEFUN(gtp_echo_tx_local_dev, gtp_echo_tx_local_dev_cmd, + "gtp1u-echo send to " VTY_IPV46_CMD " local-dev DEV_NAME", + GTP_ECHO_TX_STR + "Send from local GTP device, chosen by name as configured in 'dev create' or 'dev use'.\n" + "A GTP device name as it appears in the cfg\n") +{ + return _gtp_echo_tx(vty, 2, argc, argv); +} + void upf_vty_init() { OSMO_ASSERT(g_upf != NULL); @@ -495,6 +595,9 @@ install_element_ve(&show_session_cmd); install_element_ve(&show_netinst_cmd); install_element_ve(&show_nft_rule_append_cmd); + install_element_ve(>p_echo_tx_cmd); + install_element_ve(>p_echo_tx_local_ip_cmd); + install_element_ve(>p_echo_tx_local_dev_cmd);
install_node(&cfg_pfcp_node, config_write_pfcp); install_element(CONFIG_NODE, &cfg_pfcp_cmd); diff --git a/tests/gtp-echo.vty b/tests/gtp-echo.vty new file mode 100644 index 0000000..e8ffa9c --- /dev/null +++ b/tests/gtp-echo.vty @@ -0,0 +1,42 @@ +OsmoUPF> list +... + gtp1u-echo send to (A.B.C.D|X:X::X:X) + gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X) + gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME +... +OsmoUPF> enable +OsmoUPF# list +... + gtp1u-echo send to (A.B.C.D|X:X::X:X) + gtp1u-echo send to (A.B.C.D|X:X::X:X) local-ip (A.B.C.D|X:X::X:X) + gtp1u-echo send to (A.B.C.D|X:X::X:X) local-dev DEV_NAME +... +OsmoUPF# configure terminal +OsmoUPF(config)# list +... !gtp1u-echo +OsmoUPF(config)# end + +OsmoUPF# gtp1u-echo? + gtp1u-echo GTP1-U Echo probing +OsmoUPF# gtp1u-echo ? + send Send a GTP1-U Echo Request to a remote peer +OsmoUPF# gtp1u-echo send ? + to Send to remote peer's GTP address +OsmoUPF# gtp1u-echo send to ? + A.B.C.D IPv4 address + X:X::X:X IPv6 address +OsmoUPF# gtp1u-echo send to 1.2.3.4 ? + local-ip Send from local GTP device, chosen by IP address + local-dev Send from local GTP device, chosen by name as configured in 'dev create' or 'dev use'. + <cr> +OsmoUPF# gtp1u-echo send to 1.2.3.4 local-ip ? + A.B.C.D IPv4 address + X:X::X:X IPv6 address +OsmoUPF# gtp1u-echo send to 1.2.3.4 local-dev ? + DEV_NAME A GTP device name as it appears in the cfg +OsmoUPF# gtp1u-echo send to 1.2.3.4 +% Error: cannot send Echo: there is no GTP device +OsmoUPF# gtp1u-echo send to 1.2.3.4 local-ip 1.2.3.4 +% Error: cannot send Echo: this does not seem to be a locally bound GTP address: 1.2.3.4:2152 +OsmoUPF# gtp1u-echo send to 1.2.3.4 local-dev apn0 +% Error: cannot send Echo: there is no GTP device by the name of 'apn0'