[ovs-dev] [PATCH ovn 2/2] Add sctp_abort logical flow action.

Mark Michelson mmichels at redhat.com
Wed Dec 9 18:52:31 UTC 2020


This is used in similar situations as tcp_reset, only for SCTP flows.

The "router port unreachable" test has been expanded to test SCTP. In
doing so, two test cases were removed because they specified SCTP as the
protocol and expected ICMP replies.

Signed-off-by: Mark Michelson <mmichels at redhat.com>
---
 controller/pinctrl.c  |  5 +++
 include/ovn/actions.h |  7 ++++
 lib/actions.c         | 22 ++++++++++++
 northd/ovn-northd.c   | 28 +++++++++++++--
 tests/ovn.at          | 84 +++++++++++++++++++++++++++++++++++++++++--
 utilities/ovn-trace.c |  5 +++
 6 files changed, 147 insertions(+), 4 deletions(-)

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index d121a0f48..5fb0c1175 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -3039,6 +3039,11 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
                                  &userdata, false);
         break;
 
+    case ACTION_OPCODE_SCTP_ABORT:
+        pinctrl_handle_sctp_abort(swconn, &headers, &packet,
+                                  &pin.flow_metadata, &userdata, false);
+        break;
+
     case ACTION_OPCODE_REJECT:
         pinctrl_handle_reject(swconn, &headers, &packet, &pin.flow_metadata,
                               &userdata);
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 9c1ebf4aa..7181f9109 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -105,6 +105,7 @@ struct ovn_extend_table;
     OVNACT(CHK_LB_HAIRPIN,    ovnact_result)          \
     OVNACT(CHK_LB_HAIRPIN_REPLY, ovnact_result)       \
     OVNACT(CT_SNAT_TO_VIP,    ovnact_null)            \
+    OVNACT(SCTP_ABORT,        ovnact_nest)            \
 
 /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
 enum OVS_PACKED_ENUM ovnact_type {
@@ -627,6 +628,12 @@ enum action_opcode {
      * The actions, in OpenFlow 1.3 format, follow the action_header.
      */
     ACTION_OPCODE_REJECT,
+
+    /* "sctp_abort { ...actions... }".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_SCTP_ABORT,
 };
 
 /* Header. */
diff --git a/lib/actions.c b/lib/actions.c
index 0705e4ef3..f594a9e5c 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1488,6 +1488,12 @@ parse_TCP_RESET(struct action_context *ctx)
     parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp", ctx->scope);
 }
 
+static void
+parse_SCTP_ABORT(struct action_context *ctx)
+{
+    parse_nested_action(ctx, OVNACT_SCTP_ABORT, "sctp", ctx->scope);
+}
+
 static void
 parse_ND_NA(struct action_context *ctx)
 {
@@ -1569,6 +1575,12 @@ format_TCP_RESET(const struct ovnact_nest *nest, struct ds *s)
     format_nested_action(nest, "tcp_reset", s);
 }
 
+static void
+format_SCTP_ABORT(const struct ovnact_nest *nest, struct ds *s)
+{
+    format_nested_action(nest, "sctp_abort", s);
+}
+
 static void
 format_ND_NA(const struct ovnact_nest *nest, struct ds *s)
 {
@@ -1698,6 +1710,14 @@ encode_TCP_RESET(const struct ovnact_nest *on,
     encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts);
 }
 
+static void
+encode_SCTP_ABORT(const struct ovnact_nest *on,
+                  const struct ovnact_encode_params *ep,
+                  struct ofpbuf *ofpacts)
+{
+    encode_nested_actions(on, ep, ACTION_OPCODE_SCTP_ABORT, ofpacts);
+}
+
 static void
 encode_REJECT(const struct ovnact_nest *on,
               const struct ovnact_encode_params *ep,
@@ -3810,6 +3830,8 @@ parse_action(struct action_context *ctx)
         ovnact_put_IGMP(ctx->ovnacts);
     } else if (lexer_match_id(ctx->lexer, "tcp_reset")) {
         parse_TCP_RESET(ctx);
+    } else if (lexer_match_id(ctx->lexer, "sctp_abort")) {
+        parse_SCTP_ABORT(ctx);
     } else if (lexer_match_id(ctx->lexer, "nd_na")) {
         parse_ND_NA(ctx);
     } else if (lexer_match_id(ctx->lexer, "nd_na_router")) {
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 957242367..45d891ebe 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -8987,7 +8987,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
         if (!smap_get(&op->od->nbr->options, "chassis")
             && !op->od->l3dgw_port) {
-            /* UDP/TCP port unreachable. */
+            /* UDP/TCP/SCTP port unreachable. */
             for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
                 ds_clear(&match);
                 ds_put_format(&match,
@@ -9016,6 +9016,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                         80, ds_cstr(&match), action,
                                         &op->nbrp->header_);
 
+                ds_clear(&match);
+                ds_put_format(&match,
+                              "ip4 && ip4.dst == %s && !ip.later_frag && sctp",
+                              op->lrp_networks.ipv4_addrs[i].addr_s);
+                action = "sctp_abort {"
+                         "eth.dst <-> eth.src; "
+                         "ip4.dst <-> ip4.src; "
+                         "next; };";
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(&match), action,
+                                        &op->nbrp->header_);
+
                 ds_clear(&match);
                 ds_put_format(&match,
                               "ip4 && ip4.dst == %s && !ip.later_frag",
@@ -11068,7 +11080,7 @@ build_ipv6_input_flows_for_lrouter_port(
                                   &op->nbrp->header_, lflows);
         }
 
-        /* UDP/TCP port unreachable */
+        /* UDP/TCP/SCTP port unreachable */
         if (!smap_get(&op->od->nbr->options, "chassis")
             && !op->od->l3dgw_port) {
             for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
@@ -11084,6 +11096,18 @@ build_ipv6_input_flows_for_lrouter_port(
                                         80, ds_cstr(match), action,
                                         &op->nbrp->header_);
 
+                ds_clear(match);
+                ds_put_format(match,
+                              "ip6 && ip6.dst == %s && !ip.later_frag && sctp",
+                              op->lrp_networks.ipv6_addrs[i].addr_s);
+                action = "sctp_abort {"
+                         "eth.dst <-> eth.src; "
+                         "ip6.dst <-> ip6.src; "
+                         "next; };";
+                ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT,
+                                        80, ds_cstr(match), action,
+                                        &op->nbrp->header_);
+
                 ds_clear(match);
                 ds_put_format(match,
                               "ip6 && ip6.dst == %s && !ip.later_frag && udp",
diff --git a/tests/ovn.at b/tests/ovn.at
index 6c3d4ffc4..80c45eeda 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1637,6 +1637,17 @@ tcp_reset { };
     encodes as controller(userdata=00.00.00.0b.00.00.00.00)
     has prereqs tcp
 
+# sctp_abort
+sctp_abort {eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
+    formats as sctp_abort { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
+    encodes as controller(userdata=00.00.00.17.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
+    has prereqs sctp
+
+sctp_abort { };
+    formats as sctp_abort { drop; };
+    encodes as controller(userdata=00.00.00.17.00.00.00.00)
+    has prereqs sctp
+
 # reject
 reject { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
     encodes as controller(userdata=00.00.00.16.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
@@ -14337,6 +14348,45 @@ test_tcp_syn_packet() {
     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
 }
 
+# test_sctp_init_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM SCTP_SPORT SCTP_DPORT SCTP_INIT_TAG SCTP_CHKSUM EXP_IP_CHKSUM EXP_SCTP_ABORT_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an SCTP INIT chunk with
+# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHKSUM, SCTP_SPORT, SCTP_DPORT, and SCTP_CHKSUM as specified.
+# The INIT "initiate_tag" will be set to SCTP_INIT_TAG.
+# EXP_IP_CHKSUM and EXP_SCTP_CHKSUM are the ip and sctp checksums of the SCTP ABORT chunk generated by OVN logical router
+#
+# INPORT is an lport number, e.g. 1 for vif1.
+# HV is a hypervisor number.
+# ETH_SRC and ETH_DST are each 12 hex digits.
+# IPV4_SRC and IPV4_DST are each 8 hex digits.
+# SCTP_SPORT and SCTP_DPORT are 4 hex digits.
+# IP_CHKSUM and EXP_IP_CHKSUM are 4 hex digits.
+# SCTP_CHKSUM and EXP_SCTP_CHKSUM are 8 hex digits.
+test_sctp_init_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7
+    local sctp_sport=$8 sctp_dport=$9 sctp_init_tag=${10} sctp_chksum=${11}
+    local exp_ip_chksum=${12} exp_sctp_abort_chksum=${13}
+
+    local ip_ttl=ff
+    local eth_hdr=${eth_dst}${eth_src}0800
+    local ip_hdr=4500002500004000${ip_ttl}84${ip_chksum}${ipv4_src}${ipv4_dst}
+    local sctp_hdr=${sctp_sport}${sctp_dport}00000000${sctp_chksum}
+    local sctp_init=01000014${sctp_init_tag}0000000000010001${sctp_init_tag}
+
+    local packet=${eth_hdr}${ip_hdr}${sctp_hdr}${sctp_init}
+
+    local sctp_abort_ttl=3e
+    local reply_eth_hdr=${eth_src}${eth_dst}0800
+    local reply_ip_hdr=4500002400004000${sctp_abort_ttl}84${exp_ip_chksum}${ipv4_dst}${ipv4_src}
+    local reply_sctp_hdr=${sctp_dport}${sctp_sport}${sctp_init_tag}${exp_sctp_abort_chksum}
+    local reply_sctp_abort=06000004
+
+    local reply=${reply_eth_hdr}${reply_ip_hdr}${reply_sctp_hdr}${reply_sctp_abort}
+    echo $reply >> vif$inport.expected
+
+    check as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
 # test_tcp6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_ROUTER TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_TCP_RST_CHKSUM
 #
 # Causes a packet to be received on INPORT of the hypervisor HV. The packet is a TCP syn segment with
@@ -14357,6 +14407,36 @@ test_tcp6_packet() {
     as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
 }
 
+# test_tcp6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_ROUTER SCTP_SPORT SCTP_DPORT SCTP_INIT_TAG SCTP_CHKSUM EXP_SCTP_ABORT_CHKSUM
+#
+# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an SCTP INIT chunk with
+# ETH_SRC, ETH_DST, IPV6_SRC, IPV6_ROUTER, SCTP_SPORT, SCTP_DPORT and SCTP_CHKSUM as specified.
+# The INIT "initiate_tag" will be set to SCTP_INIT_TAG.
+# EXP_SCTP_CHKSUM is the sctp checksum of the SCTP ABORT chunk generated by OVN logical router
+test_sctp6_packet() {
+    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_router=$6
+    local sctp_sport=$7 sctp_dport=$8 sctp_init_tag=$9 sctp_chksum=${10}
+    local exp_sctp_abort_chksum=${11}
+    shift 11
+
+    local eth_hdr=${eth_dst}${eth_src}86dd
+    local ip_hdr=60000000002084ff${ipv6_src}${ipv6_router}
+    local sctp_hdr=${sctp_sport}${sctp_dport}00000000${sctp_chksum}
+    local sctp_init=01000014${sctp_init_tag}0000000000010001${sctp_init_tag}
+
+    local packet=${eth_hdr}${ip_hdr}${sctp_hdr}${sctp_init}
+
+    local reply_eth_hdr=${eth_src}${eth_dst}86dd
+    local reply_ip_hdr=600000000010843e${ipv6_router}${ipv6_src}
+    local reply_sctp_hdr=${sctp_dport}${sctp_sport}${sctp_init_tag}${exp_sctp_abort_chksum}
+    local reply_sctp_abort=06000004
+
+    local reply=${reply_eth_hdr}${reply_ip_hdr}${reply_sctp_hdr}${reply_sctp_abort}
+    echo $reply >> vif$inport.expected
+
+    check as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
+}
+
 # test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_PROTO IPV6_LEN DATA EXP_ICMP_CODE EXP_ICMP_CHKSUM
 #
 # Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6
@@ -14411,13 +14491,13 @@ OVN_POPULATE_ARP
 ovn-nbctl --wait=hv sync
 
 test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 11 0000 f87c f485 0303
-test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 84 0000 f87c f413 0302
 test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000100000000000000000001 11 0015 dbb8303900155bac6b646f65206676676e6d66720a 0104 1d31
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
 
 test_tcp_syn_packet 2 2 000000000002 00000000ff02 $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 2 254) 0000 8b40 3039 0000 b680 6e05
-test_ip6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 84 0004 01020304 0103 5e74
+test_sctp_init_packet 2 2 000000000002 00000000ff02 $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 2 254) 0000 8b40 3039 00000001 82112601 b606 10fe95b6
 test_tcp6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 8b40 3039 0000 98cd
+test_sctp6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 8b40 3039 00000002 C0379D5A 39f23aaf
 OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [vif2.expected])
 
 OVN_CLEANUP([hv1], [hv2])
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index 08f7dfece..5e266ab83 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -2590,6 +2590,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
                               false, pipeline, super);
             break;
 
+        case OVNACT_SCTP_ABORT:
+            execute_sctp_abort(ovnact_get_SCTP_ABORT(a), dp, uflow, table_id,
+                               false, pipeline, super);
+            break;
+
         case OVNACT_OVNFIELD_LOAD:
             execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super);
             break;
-- 
2.25.4



More information about the dev mailing list