Initial integration of SPGW in fabric.p4
Change-Id: Idd78399212039e44c982f50d343f824d516f938a
diff --git a/pipelines/fabric/src/main/resources/include/checksum.p4 b/pipelines/fabric/src/main/resources/include/checksum.p4
index 00b4e70..4d1782d 100644
--- a/pipelines/fabric/src/main/resources/include/checksum.p4
+++ b/pipelines/fabric/src/main/resources/include/checksum.p4
@@ -17,6 +17,10 @@
#ifndef __CHECKSUM__
#define __CHECKSUM__
+#ifdef WITH_SPGW
+#include "spgw.p4"
+#endif // WITH_SPGW
+
control FabricComputeChecksum(inout parsed_headers_t hdr,
inout fabric_metadata_t meta)
{
@@ -38,6 +42,9 @@
hdr.ipv4.hdr_checksum,
HashAlgorithm.csum16
);
+#ifdef WITH_SPGW
+ update_gtpu_checksum.apply(hdr.gtpu_ipv4);
+#endif // WITH_SPGW
}
}
@@ -62,6 +69,9 @@
hdr.ipv4.hdr_checksum,
HashAlgorithm.csum16
);
+#ifdef WITH_SPGW
+ verify_gtpu_checksum.apply(hdr.gtpu_ipv4);
+#endif // WITH_SPGW
}
}
diff --git a/pipelines/fabric/src/main/resources/include/define.p4 b/pipelines/fabric/src/main/resources/include/define.p4
index 4765824..d3a7811 100644
--- a/pipelines/fabric/src/main/resources/include/define.p4
+++ b/pipelines/fabric/src/main/resources/include/define.p4
@@ -44,6 +44,8 @@
const bit<8> PROTO_UDP = 17;
const bit<8> PROTO_ICMPV6 = 58;
+const bit<4> IPV4_MIN_IHL = 5;
+
#ifndef CPU_PORT
const port_num_t CPU_PORT = 255;
#endif
@@ -56,5 +58,29 @@
const fwd_type_t FWD_IPV6_MULTICAST = 5;
const bit<8> DEFAULT_MPLS_TTL = 64;
+const bit<8> DEFAULT_IPV4_TTL = 64;
+
+#define ETH_HDR_SIZE 14
+#define IPV4_HDR_SIZE 20
+#define UDP_HDR_SIZE 8
+
+#define UDP_PORT_GTPU 2152
+#define GTP_GPDU 0xff
+#define GTPU_VERSION 0x01
+#define GTP_PROTOCOL_TYPE_GTP 0x01
+
+typedef bit direction_t;
+typedef bit pcc_gate_status_t;
+typedef bit<32> sdf_rule_id_t;
+typedef bit<32> pcc_rule_id_t;
+
+const sdf_rule_id_t DEFAULT_SDF_RULE_ID = 0;
+const pcc_rule_id_t DEFAULT_PCC_RULE_ID = 0;
+
+const direction_t DIR_UPLINK = 1w0;
+const direction_t DIR_DOWNLINK = 1w1;
+
+const pcc_gate_status_t PCC_GATE_OPEN = 1w0;
+const pcc_gate_status_t PCC_GATE_CLOSED = 1w1;
#endif
diff --git a/pipelines/fabric/src/main/resources/include/header.p4 b/pipelines/fabric/src/main/resources/include/header.p4
index c177ad5..b9f36b1 100644
--- a/pipelines/fabric/src/main/resources/include/header.p4
+++ b/pipelines/fabric/src/main/resources/include/header.p4
@@ -115,6 +115,34 @@
bit<64> timestamp;
}
+#ifdef WITH_SPGW
+// GTPU v1
+header gtpu_t {
+ bit<3> version; /* version */
+ bit<1> pt; /* protocol type */
+ bit<1> spare; /* reserved */
+ bit<1> ex_flag; /* next extension hdr present? */
+ bit<1> seq_flag; /* sequence no. */
+ bit<1> npdu_flag; /* n-pdn number present ? */
+ bit<8> msgtype; /* message type */
+ bit<16> msglen; /* message length */
+ bit<32> teid; /* tunnel endpoint id */
+}
+
+struct spgw_meta_t {
+ bool do_spgw;
+ bit<16> l4_src_port;
+ bit<16> l4_dst_port;
+ direction_t direction;
+ pcc_gate_status_t pcc_gate_status;
+ sdf_rule_id_t sdf_rule_id;
+ pcc_rule_id_t pcc_rule_id;
+ bit<32> dl_sess_teid;
+ bit<32> dl_sess_enb_addr;
+ bit<32> dl_sess_s1u_addr;
+}
+#endif // WITH_SPGW
+
//Custom metadata definition
struct fabric_metadata_t {
fwd_type_t fwd_type;
@@ -124,12 +152,20 @@
bit<16> l4_src_port;
bit<16> l4_dst_port;
bit<16> original_ether_type;
+#ifdef WITH_SPGW
+ spgw_meta_t spgw;
+#endif // WITH_SPGW
}
struct parsed_headers_t {
ethernet_t ethernet;
vlan_tag_t vlan_tag;
mpls_t mpls;
+#ifdef WITH_SPGW
+ ipv4_t gtpu_ipv4;
+ udp_t gtpu_udp;
+ gtpu_t gtpu;
+#endif // WITH_SPGW
ipv4_t ipv4;
ipv6_t ipv6;
arp_t arp;
diff --git a/pipelines/fabric/src/main/resources/include/parser.p4 b/pipelines/fabric/src/main/resources/include/parser.p4
index 67cc443..8355c12 100644
--- a/pipelines/fabric/src/main/resources/include/parser.p4
+++ b/pipelines/fabric/src/main/resources/include/parser.p4
@@ -113,13 +113,46 @@
packet.extract(hdr.udp);
fabric_metadata.l4_src_port = hdr.udp.src_port;
fabric_metadata.l4_dst_port = hdr.udp.dst_port;
+#ifdef WITH_SPGW
+ transition select(hdr.udp.dst_port) {
+ UDP_PORT_GTPU: parse_gtpu;
+ default: accept;
+ }
+#else
transition accept;
+#endif // WITH_SPGW
}
state parse_icmp {
packet.extract(hdr.icmp);
transition accept;
}
+
+#ifdef WITH_SPGW
+ state parse_gtpu {
+ packet.extract(hdr.gtpu);
+ transition parse_ipv4_inner;
+ }
+
+ state parse_ipv4_inner {
+ hdr.gtpu_ipv4 = hdr.ipv4;
+ packet.extract(hdr.ipv4);
+ transition select(hdr.ipv4.protocol) {
+ PROTO_TCP: parse_tcp;
+ PROTO_UDP: parse_udp_inner;
+ PROTO_ICMP: parse_icmp;
+ default: accept;
+ }
+ }
+
+ state parse_udp_inner {
+ hdr.gtpu_udp = hdr.udp;
+ packet.extract(hdr.udp);
+ fabric_metadata.l4_src_port = hdr.udp.src_port;
+ fabric_metadata.l4_dst_port = hdr.udp.dst_port;
+ transition accept;
+ }
+#endif // WITH_SPGW
}
control FabricDeparser(packet_out packet, in parsed_headers_t hdr) {
@@ -129,6 +162,11 @@
packet.emit(hdr.vlan_tag);
packet.emit(hdr.mpls);
packet.emit(hdr.arp);
+#ifdef WITH_SPGW
+ packet.emit(hdr.gtpu_ipv4);
+ packet.emit(hdr.gtpu_udp);
+ packet.emit(hdr.gtpu);
+#endif // WITH_SPGW
packet.emit(hdr.ipv4);
packet.emit(hdr.ipv6);
packet.emit(hdr.tcp);
diff --git a/pipelines/fabric/src/main/resources/include/spgw.p4 b/pipelines/fabric/src/main/resources/include/spgw.p4
new file mode 100644
index 0000000..ce39acb
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/include/spgw.p4
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SPGW__
+#define __SPGW__
+
+
+control spgw_ingress(
+ inout ipv4_t gtpu_ipv4,
+ inout udp_t gtpu_udp,
+ inout gtpu_t gtpu,
+ inout spgw_meta_t spgw_meta,
+ in ipv4_t ipv4
+ ) {
+
+ direct_counter(CounterType.packets_and_bytes) ue_counter;
+
+ action gtpu_decap() {
+ gtpu_ipv4.setInvalid();
+ gtpu_udp.setInvalid();
+ gtpu.setInvalid();
+ }
+
+ action set_sdf_rule_id(sdf_rule_id_t id) {
+ spgw_meta.sdf_rule_id = id;
+ }
+
+ action set_pcc_rule_id(pcc_rule_id_t id) {
+ spgw_meta.pcc_rule_id = id;
+ }
+
+ action set_pcc_info(pcc_gate_status_t gate_status) {
+ spgw_meta.pcc_gate_status = gate_status;
+ }
+
+ action set_dl_sess_info(bit<32> dl_sess_teid,
+ bit<32> dl_sess_enb_addr,
+ bit<32> dl_sess_s1u_addr) {
+ spgw_meta.dl_sess_teid = dl_sess_teid;
+ spgw_meta.dl_sess_enb_addr = dl_sess_enb_addr;
+ spgw_meta.dl_sess_s1u_addr = dl_sess_s1u_addr;
+ }
+
+ action update_ue_cdr() {
+ ue_counter.count();
+ }
+
+ table ue_filter_table {
+ key = {
+ // IP prefixes of the UEs managed by this switch.
+ ipv4.dst_addr : lpm;
+ }
+ actions = {
+ NoAction();
+ }
+ }
+
+ table s1u_filter_table {
+ key = {
+ // IP addresses of the S1U interfaces embodied by this switch.
+ gtpu_ipv4.dst_addr : exact;
+ }
+ actions = {
+ NoAction();
+ }
+ }
+
+ table sdf_rule_lookup {
+ key = {
+ spgw_meta.direction : exact;
+ ipv4.src_addr : ternary;
+ ipv4.dst_addr : ternary;
+ ipv4.protocol : ternary;
+ spgw_meta.l4_src_port : ternary;
+ spgw_meta.l4_dst_port : ternary;
+ }
+ actions = {
+ set_sdf_rule_id();
+ }
+ const default_action = set_sdf_rule_id(DEFAULT_SDF_RULE_ID);
+ }
+
+ table pcc_rule_lookup {
+ key = {
+ spgw_meta.sdf_rule_id : exact;
+ }
+ actions = {
+ set_pcc_rule_id();
+ }
+ const default_action = set_pcc_rule_id(DEFAULT_PCC_RULE_ID);
+ }
+
+ table pcc_info_lookup {
+ key = {
+ spgw_meta.pcc_rule_id : exact;
+ }
+ actions = {
+ set_pcc_info();
+ }
+ const default_action = set_pcc_info(PCC_GATE_OPEN);
+ }
+
+ table dl_sess_lookup {
+ key = {
+ // UE addr for downlink
+ ipv4.dst_addr : exact;
+ }
+ actions = {
+ set_dl_sess_info();
+ }
+ }
+
+ table ue_cdr_table {
+ key = {
+ // UE addr for downlink
+ ipv4.dst_addr : exact;
+ }
+ actions = {
+ update_ue_cdr();
+ }
+ counters = ue_counter;
+ }
+
+ apply {
+ // Admit only packets to known UE/S1U addresses, if so sets direction,
+ // otherwise skip SPGW processing.
+ spgw_meta.do_spgw = false;
+ if (gtpu.isValid() && gtpu.msgtype == GTP_GPDU) {
+ spgw_meta.direction = DIR_UPLINK;
+ if (s1u_filter_table.apply().hit) {
+ spgw_meta.do_spgw = true;
+ }
+ } else {
+ if (ue_filter_table.apply().hit) {
+ spgw_meta.do_spgw = true;
+ }
+ }
+
+ if (!spgw_meta.do_spgw) {
+ // Exit this control block.
+ return;
+ }
+
+ if (spgw_meta.direction == DIR_UPLINK) {
+ gtpu_decap();
+ }
+
+ // Allow all traffic by default.
+ spgw_meta.pcc_gate_status = PCC_GATE_OPEN;
+
+ sdf_rule_lookup.apply();
+ pcc_rule_lookup.apply();
+ pcc_info_lookup.apply();
+
+ if (spgw_meta.pcc_gate_status == PCC_GATE_CLOSED) {
+ mark_to_drop();
+ exit;
+ }
+
+ if (spgw_meta.direction == DIR_DOWNLINK) {
+ if (!dl_sess_lookup.apply().hit) {
+ // We have no other choice than drop, as we miss the session
+ // info necessary to properly GTPU encap the packet.
+ mark_to_drop();
+ exit;
+ }
+ ue_cdr_table.apply();
+ }
+ }
+}
+
+
+control spgw_egress(
+ out ipv4_t gtpu_ipv4,
+ out udp_t gtpu_udp,
+ out gtpu_t gtpu,
+ in spgw_meta_t spgw_meta,
+ in standard_metadata_t std_meta
+ ) {
+
+ action gtpu_encap() {
+ // GTPU
+ gtpu.setValid();
+ gtpu.version = GTPU_VERSION;
+ gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
+ gtpu.spare = 0;
+ gtpu.ex_flag = 0;
+ gtpu.seq_flag = 0;
+ gtpu.npdu_flag = 0;
+ gtpu.msgtype = GTP_GPDU;
+ gtpu.msglen = (bit<16>) (std_meta.packet_length - ETH_HDR_SIZE);
+ gtpu.teid = spgw_meta.dl_sess_teid;
+ // Outer IPv4
+ gtpu_ipv4.setValid();
+ gtpu_ipv4.version = IP_VERSION_4;
+ gtpu_ipv4.ihl = IPV4_MIN_IHL;
+ gtpu_ipv4.diffserv = 0;
+ gtpu_ipv4.total_len = (bit<16>) (std_meta.packet_length
+ - ETH_HDR_SIZE + IPV4_HDR_SIZE + UDP_HDR_SIZE);
+ gtpu_ipv4.identification = 0x1513; /* From NGIC */
+ gtpu_ipv4.flags = 0;
+ gtpu_ipv4.frag_offset = 0;
+ gtpu_ipv4.ttl = DEFAULT_IPV4_TTL;
+ gtpu_ipv4.protocol = PROTO_UDP;
+ gtpu_ipv4.dst_addr = spgw_meta.dl_sess_enb_addr;
+ gtpu_ipv4.src_addr = spgw_meta.dl_sess_s1u_addr;
+ gtpu_ipv4.hdr_checksum = 0; /* Updated later */
+ // Outer UDP
+ gtpu_udp.setValid();
+ gtpu_udp.src_port = UDP_PORT_GTPU;
+ gtpu_udp.dst_port = UDP_PORT_GTPU;
+ gtpu_udp.len = (bit<16>) (std_meta.packet_length
+ - ETH_HDR_SIZE + UDP_HDR_SIZE);
+ gtpu_udp.checksum = 0; /* Ignore, won't be updated */
+ }
+
+ apply {
+ if (spgw_meta.do_spgw && spgw_meta.direction == DIR_DOWNLINK) {
+ gtpu_encap();
+ }
+ }
+}
+
+
+control verify_gtpu_checksum(inout ipv4_t gtpu_ipv4) {
+ apply {
+ verify_checksum(gtpu_ipv4.isValid(),
+ {
+ gtpu_ipv4.version,
+ gtpu_ipv4.ihl,
+ gtpu_ipv4.diffserv,
+ gtpu_ipv4.total_len,
+ gtpu_ipv4.identification,
+ gtpu_ipv4.flags,
+ gtpu_ipv4.frag_offset,
+ gtpu_ipv4.ttl,
+ gtpu_ipv4.protocol,
+ gtpu_ipv4.src_addr,
+ gtpu_ipv4.dst_addr
+ },
+ gtpu_ipv4.hdr_checksum,
+ HashAlgorithm.csum16
+ );
+ }
+}
+
+
+control update_gtpu_checksum(inout ipv4_t gtpu_ipv4) {
+ apply {
+ // Compute outer IPv4 checksum.
+ update_checksum(gtpu_ipv4.isValid(),
+ {
+ gtpu_ipv4.version,
+ gtpu_ipv4.ihl,
+ gtpu_ipv4.diffserv,
+ gtpu_ipv4.total_len,
+ gtpu_ipv4.identification,
+ gtpu_ipv4.flags,
+ gtpu_ipv4.frag_offset,
+ gtpu_ipv4.ttl,
+ gtpu_ipv4.protocol,
+ gtpu_ipv4.src_addr,
+ gtpu_ipv4.dst_addr
+ },
+ gtpu_ipv4.hdr_checksum,
+ HashAlgorithm.csum16
+ );
+ }
+}
+
+#endif