The GTP-U optional field (sequence number, N-PDU number and next
extension header type) is present when any of the E, S or PN flags is
set (3GPP TS 29.281, section 5.1), but netdev_gtpu_pop_header() only
looks at the S flag. It also does not skip the extension headers when
the E flag is set.
On the 5G N3 interface, G-PDUs usually have only the E flag set and
carry a PDU Session Container (3GPP TS 38.415). These packets are
decapsulated at the wrong offset: the optional field and the extension
header are taken as the inner packet, which is then dropped as a
non-IP packet.
Take the optional field into account for the E and PN flags too, and
walk the extension header chain, using the length of each header, in
4-octet units, and the next type in its last octet. Drop the packet if
an extension header is empty or does not fit in the packet.
Fixes: 3c6d05a02e0f ("userspace: Add GTP-U support.")
Assisted-by: LLM
Signed-off-by: Anil Kaushik <[email protected]>
---
Notes:
This applies on top of the pending "netdev-native-tnl: Check GTP-U
header length on pop." from Alejandro Pacheco, patch 05/11 of his v2
"Fix header length checks in the userspace datapath":
https://patchwork.ozlabs.org/project/openvswitch/patch/CAB8Pm=yz4mtbctzogj7dhxxtaobcqrqva_btlszotlnfwau...@mail.gmail.com/
It does not apply on main without it.
lib/netdev-native-tnl.c | 34 ++++++++++++++--
lib/packets.h | 1 +
tests/tunnel-push-pop.at | 83 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index dc14542ec..8205cef4c 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -838,16 +838,44 @@ netdev_gtpu_pop_header(struct dp_packet *packet)
tnl->tun_id = be32_to_be64(get_16aligned_be32(>ph->teid));
if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
+ size_t l4_size = dp_packet_l4_size(packet);
+ const uint8_t *l4 = dp_packet_l4(packet);
uint8_t ip_ver = 0;
+ /* The optional field is present if any of the E, S or PN flags is
+ * set, not only S. */
gtpu_hlen = GTPU_HLEN;
- if (gtph->md.flags & GTPU_S_MASK) {
+ if (gtph->md.flags & (GTPU_E_MASK | GTPU_S_MASK | GTPU_PN_MASK)) {
gtpu_hlen += sizeof(struct gtpuhdr_opt);
}
- if (gtpu_hlen > dp_packet_l4_size(packet)) {
+ if (gtpu_hlen > l4_size) {
goto err;
}
- if (gtpu_hlen < dp_packet_l4_size(packet)) {
+
+ /* Skip the extension headers, e.g. the PDU Session Container used
+ * on the 5G N3 interface. Each one has its length, in 4-octet
+ * units, in its first octet, and the type of the next one in its
+ * last octet. */
+ if (gtph->md.flags & GTPU_E_MASK) {
+ uint8_t next = l4[GTPU_HLEN + offsetof(struct gtpuhdr_opt,
+ next_ext_type)];
+
+ while (next) {
+ unsigned int ext_len;
+
+ if (gtpu_hlen >= l4_size) {
+ goto err;
+ }
+ ext_len = l4[gtpu_hlen] * 4;
+ if (!ext_len || ext_len > l4_size - gtpu_hlen) {
+ goto err;
+ }
+ gtpu_hlen += ext_len;
+ next = l4[gtpu_hlen - 1];
+ }
+ }
+
+ if (gtpu_hlen < l4_size) {
const struct ip_header *ip;
ip = ALIGNED_CAST(const struct ip_header *,
diff --git a/lib/packets.h b/lib/packets.h
index ea0bda840..a467e04ef 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1455,6 +1455,7 @@ static inline ovs_be32 get_erspan_ts(enum erspan_ts_gra
gra)
#define GTPU_P_MASK 0x10
#define GTPU_E_MASK 0x04
#define GTPU_S_MASK 0x02
+#define GTPU_PN_MASK 0x01
/* GTP-U UDP port. */
#define GTPU_DST_PORT 2152
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index ab393cfc7..39fa4cffc 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -193,6 +193,89 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep
100022eb0000000120000237 | wc -l`
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([tunnel_push_pop - gtpu decap with extension header])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy dnl
+ ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy],
+ [0])
+AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=gtpu \
+ options:remote_ip=1.1.2.92 options:key=123 \
+ ofport_request=2])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
+])
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+AT_CHECK([ovs-ofctl add-flow int-br "cookie=0x1,in_port=2,ip,actions=drop"])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 dnl
+'recirc_id(0),in_port(100),eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),dnl
+eth_type(0x0806),arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,dnl
+tha=00:00:00:00:00:00)'])
+AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore])
+AT_CHECK([ovs-appctl revalidator/wait])
+
+dnl G-PDU with only the E flag set, as sent on the 5G N3 interface: the
+dnl optional field is there, followed by a PDU Session Container (QFI 9).
+m4_define([GTPU_EXT_PKT], m4_join([],
+dnl eth(src=00:1b:21:3c:ab:64,dst=aa:55:aa:55:00:00,type=0x0800)
+[aa55aa550000001b213cab640800],
+dnl ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tot_len=72)
+[4500004800010000401173ef0101025c01010258],
+dnl udp(src=4789,dst=2152,len=52)
+[12b5086800340000],
+dnl gtpu(flags=0x34,msgtype=255,len=36,teid=123)
+[34ff00240000007b],
+dnl gtpu optional field: next extension is a PDU Session Container.
+[00000085],
+dnl PDU Session Container: len=1, UL PDU, QFI=9, no next extension.
+[01100900],
+dnl ipv4(src=30.0.0.1,dst=30.0.0.2,proto=1,tot_len=28)
+[4500001c000100008001fedd1e0000011e000002],
+dnl icmp(type=0,code=0)
+[0000ffff00000000]))
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 GTPU_EXT_PKT])
+AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore])
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 2'], [0], [dnl
+ port 2: rx pkts=1, bytes=28, drop=?, errs=?, frame=?, over=?, crc=?
+])
+AT_CHECK([ovs-ofctl dump-flows int-br | grep 'cookie=0x1,' | dnl
+ grep -c 'n_packets=1,'], [0], [1
+])
+
+dnl Same packet, but with an extension header length of 0: dropped.
+m4_define([GTPU_EXT_LEN0_PKT], m4_join([],
+[aa55aa550000001b213cab640800],
+[4500004800010000401173ef0101025c01010258],
+[12b5086800340000],
+[34ff00240000007b],
+[00000085],
+dnl PDU Session Container with len=0.
+[00100900],
+[4500001c000100008001fedd1e0000011e000002],
+[0000ffff00000000]))
+
+dnl The optional field announces an extension header that is missing: dropped.
+m4_define([GTPU_EXT_TRUNC_PKT], m4_join([],
+[aa55aa550000001b213cab640800],
+dnl ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tot_len=40)
+[45000028000100004011740f0101025c01010258],
+dnl udp(src=4789,dst=2152,len=20)
+[12b5086800140000],
+dnl gtpu(flags=0x34,msgtype=255,len=4,teid=123)
+[34ff00040000007b],
+[00000085]))
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 GTPU_EXT_LEN0_PKT])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 GTPU_EXT_TRUNC_PKT])
+AT_CHECK([ovs-appctl time/warp 1000], [0], [ignore])
+AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 2'], [0], [dnl
+ port 2: rx pkts=1, bytes=28, drop=?, errs=?, frame=?, over=?, crc=?
+])
+CHECK_COVERAGE([datapath_drop_tunnel_pop_error], [2])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([tunnel_push_pop - action])
OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy
ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
--
2.25.1
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev