From patchwork Wed Dec 9 18:52:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1413620 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=TqCumzuc; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CrmQJ0CBFz9s0b for ; Thu, 10 Dec 2020 05:52:55 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 984C3876AD; Wed, 9 Dec 2020 18:52:53 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 0yiPrV2ASA-t; Wed, 9 Dec 2020 18:52:49 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 5307D8769C; Wed, 9 Dec 2020 18:52:48 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2BCA8C1DA2; Wed, 9 Dec 2020 18:52:48 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 32C45C013B for ; Wed, 9 Dec 2020 18:52:46 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 20F0286D7C for ; Wed, 9 Dec 2020 18:52:46 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id RRLoIhZiIzQ4 for ; Wed, 9 Dec 2020 18:52:45 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by whitealder.osuosl.org (Postfix) with ESMTPS id BD57C86D7E for ; Wed, 9 Dec 2020 18:52:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1607539963; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=fA47nlFhU65wU8bqiwjNkLynH0omep/sofeqrT2RPoE=; b=TqCumzucAos3g9vDyoX995y/3uIp7Cn3mQDpCaJrkX5bRjH3pn9mBLi3E7l2WoT4MtDGq+ LLf3cJv6Z7WLMEJP8lHqS5FhOLB26bfLoBmyC91WdelLgGxVCv7UsTtVb8Pgk5TKhZim6u 7NPSmgLuC2ilYUeoXmnRn34SuzGF8Nk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-405-LgZWk1U4Oz-ZIRZpVZMJIA-1; Wed, 09 Dec 2020 13:52:41 -0500 X-MC-Unique: LgZWk1U4Oz-ZIRZpVZMJIA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 63162100F375 for ; Wed, 9 Dec 2020 18:52:34 +0000 (UTC) Received: from monae.redhat.com (ovpn-115-14.rdu2.redhat.com [10.10.115.14]) by smtp.corp.redhat.com (Postfix) with ESMTP id 634366198C for ; Wed, 9 Dec 2020 18:52:32 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 9 Dec 2020 13:52:30 -0500 Message-Id: <20201209185231.557227-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn 1/2] Implement SCTP-specific reject() action. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" Currently in OVN, if an SCTP packet hits a reject() action, OVN responds with an ICMP packet. Instead, we should send an SCTP packet with an ABORT chunk. This will either end the current association or will prevent an association from being created, depending on which stage of the SCTP state machine we currently are in. This patch adds the desired behavior for SCTP. The reject() action will now send an SCTP ABORT if the incoming packet is SCTP. Signed-off-by: Mark Michelson --- controller/pinctrl.c | 113 ++++++++++++++++++++++++++++++++++++++++++ lib/ovn-util.h | 33 ++++++++++++ tests/ovn.at | 43 ++++++++++++++++ utilities/ovn-trace.c | 87 ++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+) diff --git a/controller/pinctrl.c b/controller/pinctrl.c index 7e3abf0a4..d121a0f48 100644 --- a/controller/pinctrl.c +++ b/controller/pinctrl.c @@ -38,6 +38,7 @@ #include "openvswitch/ofp-util.h" #include "openvswitch/vlog.h" #include "lib/random.h" +#include "lib/crc32c.h" #include "lib/dhcp.h" #include "ovn-controller.h" @@ -1763,6 +1764,116 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow, dp_packet_uninit(&packet); } +static void dp_packet_put_sctp_abort(struct dp_packet *packet, + bool reflect_tag) +{ + struct sctp_chunk_header abort = { + .sctp_chunk_type = SCTP_CHUNK_TYPE_ABORT, + .sctp_chunk_flags = reflect_tag ? SCTP_ABORT_CHUNK_FLAG_T : 0, + .sctp_chunk_len = htons(SCTP_CHUNK_HEADER_LEN), + }; + + dp_packet_put(packet, &abort, sizeof abort); +} + +static void +pinctrl_handle_sctp_abort(struct rconn *swconn, const struct flow *ip_flow, + struct dp_packet *pkt_in, + const struct match *md, struct ofpbuf *userdata, + bool loopback) +{ + if (ip_flow->nw_proto != IPPROTO_SCTP) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "SCTP_ABORT action on non-SCTP packet"); + return; + } + + struct sctp_header *sh_in = dp_packet_l4(pkt_in); + if (!sh_in) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "SCTP_ABORT action on malformed SCTP packet"); + return; + } + + const struct sctp_chunk_header *sh_in_chunk = + dp_packet_get_sctp_payload(pkt_in); + if (!sh_in_chunk) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "SCTP_ABORT action on SCTP packet with no chunks"); + return; + } + + if (sh_in_chunk->sctp_chunk_type == SCTP_CHUNK_TYPE_ABORT) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "sctp_abort action on incoming SCTP ABORT."); + return; + } + + const struct sctp_init_chunk *sh_in_init = NULL; + if (sh_in_chunk->sctp_chunk_type == SCTP_CHUNK_TYPE_INIT) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + sh_in_init = dp_packet_at(pkt_in, pkt_in->l4_ofs + + SCTP_HEADER_LEN + + SCTP_CHUNK_HEADER_LEN, + SCTP_INIT_CHUNK_LEN); + if (!sh_in_init) { + VLOG_WARN_RL(&rl, "Incomplete SCTP INIT chunk. Ignoring packet."); + return; + } + } + + uint64_t packet_stub[128 / 8]; + struct dp_packet packet; + + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); + + struct eth_addr eth_src = loopback ? ip_flow->dl_dst : ip_flow->dl_src; + struct eth_addr eth_dst = loopback ? ip_flow->dl_src : ip_flow->dl_dst; + + if (get_dl_type(ip_flow) == htons(ETH_TYPE_IPV6)) { + const struct in6_addr *ip6_src = + loopback ? &ip_flow->ipv6_dst : &ip_flow->ipv6_src; + const struct in6_addr *ip6_dst = + loopback ? &ip_flow->ipv6_src : &ip_flow->ipv6_dst; + pinctrl_compose_ipv6(&packet, eth_src, eth_dst, + (struct in6_addr *) ip6_src, + (struct in6_addr *) ip6_dst, + IPPROTO_SCTP, 63, SCTP_HEADER_LEN + + SCTP_CHUNK_HEADER_LEN); + } else { + ovs_be32 nw_src = loopback ? ip_flow->nw_dst : ip_flow->nw_src; + ovs_be32 nw_dst = loopback ? ip_flow->nw_src : ip_flow->nw_dst; + pinctrl_compose_ipv4(&packet, eth_src, eth_dst, nw_src, nw_dst, + IPPROTO_SCTP, 63, SCTP_HEADER_LEN + + SCTP_CHUNK_HEADER_LEN); + } + + struct sctp_header *sh = dp_packet_put_zeros(&packet, sizeof *sh); + dp_packet_set_l4(&packet, sh); + sh->sctp_dst = ip_flow->tp_src; + sh->sctp_src = ip_flow->tp_dst; + put_16aligned_be32(&sh->sctp_csum, 0); + + bool tag_reflected; + if (get_16aligned_be32(&sh_in->sctp_vtag) == 0 && sh_in_init) { + /* See RFC 4960 Section 8.4, item 3. */ + put_16aligned_be32(&sh->sctp_vtag, sh_in_init->initiate_tag); + tag_reflected = false; + } else { + /* See RFC 4960 Section 8.4, item 8. */ + sh->sctp_vtag = sh_in->sctp_vtag; + tag_reflected = true; + } + + dp_packet_put_sctp_abort(&packet, tag_reflected); + + put_16aligned_be32(&sh->sctp_csum, crc32c((void *) sh, + dp_packet_l4_size(&packet))); + + set_actions_and_enqueue_msg(swconn, &packet, md, userdata); + dp_packet_uninit(&packet); +} + static void pinctrl_handle_reject(struct rconn *swconn, const struct flow *ip_flow, struct dp_packet *pkt_in, @@ -1770,6 +1881,8 @@ pinctrl_handle_reject(struct rconn *swconn, const struct flow *ip_flow, { if (ip_flow->nw_proto == IPPROTO_TCP) { pinctrl_handle_tcp_reset(swconn, ip_flow, pkt_in, md, userdata, true); + } else if (ip_flow->nw_proto == IPPROTO_SCTP) { + pinctrl_handle_sctp_abort(swconn, ip_flow, pkt_in, md, userdata, true); } else { pinctrl_handle_icmp(swconn, ip_flow, pkt_in, md, userdata, true, true); } diff --git a/lib/ovn-util.h b/lib/ovn-util.h index 1d2f7a9c5..9f6a2eb39 100644 --- a/lib/ovn-util.h +++ b/lib/ovn-util.h @@ -225,4 +225,37 @@ bool ip_address_and_port_from_lb_key(const char *key, char **ip_address, * value. */ char *ovn_get_internal_version(void); + +/* OVN Packet definitions. These may eventually find a home in OVS's + * packets.h file. For the time being, they live here because OVN uses them + * and OVS does not. + */ +#define SCTP_CHUNK_HEADER_LEN 4 +struct sctp_chunk_header { + uint8_t sctp_chunk_type; + uint8_t sctp_chunk_flags; + ovs_be16 sctp_chunk_len; +}; +BUILD_ASSERT_DECL(SCTP_CHUNK_HEADER_LEN == sizeof(struct sctp_chunk_header)); + +#define SCTP_INIT_CHUNK_LEN 16 +struct sctp_init_chunk { + ovs_be32 initiate_tag; + ovs_be32 a_rwnd; + ovs_be16 num_outbound_streams; + ovs_be16 num_inbound_streams; + ovs_be32 initial_tsn; +}; +BUILD_ASSERT_DECL(SCTP_INIT_CHUNK_LEN == sizeof(struct sctp_init_chunk)); + +/* These are the only SCTP chunk types that OVN cares about. + * There is no need to define the other chunk types until they are + * needed. + */ +#define SCTP_CHUNK_TYPE_INIT 1 +#define SCTP_CHUNK_TYPE_ABORT 6 + +/* See RFC 4960 Sections 3.3.7 and 8.5.1 for information on this flag. */ +#define SCTP_ABORT_CHUNK_FLAG_T (1 << 0) + #endif diff --git a/tests/ovn.at b/tests/ovn.at index f222fd8ac..6c3d4ffc4 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -12878,6 +12878,45 @@ test_tcp_syn_packet() { check 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 from the ACL rule hit +# +# INPORT is an lport number, e.g. 11 for vif11. +# 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=3f + 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 +} + # Create hypervisors hv[123]. # Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3. # Add all of the vifs to a single logical switch sw0. @@ -12931,6 +12970,10 @@ test_tcp_syn_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(i test_tcp_syn_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 8b40 3039 0000 b85f 70e4 test_tcp_syn_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 8b40 3039 0000 b854 70d9 +test_sctp_init_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 8b40 3039 00000001 82112601 b7e5 10fe95b6 +test_sctp_init_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 8b40 3039 00000002 C0379D5A b7e5 39f23aaf +test_sctp_init_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 8b40 3039 00000003 028E263C b7da 7124045b + for i in 1 2 3; do OVN_CHECK_PACKETS([hv$i/vif${i}1-tx.pcap], [vif${i}1.expected]) done diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index 6fad36512..08f7dfece 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -1802,6 +1802,91 @@ execute_tcp_reset(const struct ovnact_nest *on, execute_tcp6_reset(on, dp, uflow, table_id, loopback, pipeline, super); } } + +static void +execute_sctp4_abort(const struct ovnact_nest *on, + const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + bool loopback, enum ovnact_pipeline pipeline, + struct ovs_list *super) +{ + struct flow sctp_flow = *uflow; + + /* Update fields for TCP SCTP. */ + if (loopback) { + sctp_flow.dl_dst = uflow->dl_src; + sctp_flow.dl_src = uflow->dl_dst; + sctp_flow.nw_dst = uflow->nw_src; + sctp_flow.nw_src = uflow->nw_dst; + } else { + sctp_flow.dl_dst = uflow->dl_dst; + sctp_flow.dl_src = uflow->dl_src; + sctp_flow.nw_dst = uflow->nw_dst; + sctp_flow.nw_src = uflow->nw_src; + } + sctp_flow.nw_proto = IPPROTO_SCTP; + sctp_flow.nw_ttl = 255; + sctp_flow.tp_src = uflow->tp_src; + sctp_flow.tp_dst = uflow->tp_dst; + + struct ovntrace_node *node = ovntrace_node_append( + super, OVNTRACE_NODE_TRANSFORMATION, "sctp_abort"); + + trace_actions(on->nested, on->nested_len, dp, &sctp_flow, + table_id, pipeline, &node->subs); +} + +static void +execute_sctp6_abort(const struct ovnact_nest *on, + const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + bool loopback, enum ovnact_pipeline pipeline, + struct ovs_list *super) +{ + struct flow sctp_flow = *uflow; + + /* Update fields for SCTP. */ + if (loopback) { + sctp_flow.dl_dst = uflow->dl_src; + sctp_flow.dl_src = uflow->dl_dst; + sctp_flow.ipv6_dst = uflow->ipv6_src; + sctp_flow.ipv6_src = uflow->ipv6_dst; + } else { + sctp_flow.dl_dst = uflow->dl_dst; + sctp_flow.dl_src = uflow->dl_src; + sctp_flow.ipv6_dst = uflow->ipv6_dst; + sctp_flow.ipv6_src = uflow->ipv6_src; + } + sctp_flow.nw_proto = IPPROTO_TCP; + sctp_flow.nw_ttl = 255; + sctp_flow.tp_src = uflow->tp_src; + sctp_flow.tp_dst = uflow->tp_dst; + sctp_flow.tcp_flags = htons(TCP_RST); + + struct ovntrace_node *node = ovntrace_node_append( + super, OVNTRACE_NODE_TRANSFORMATION, "sctp_abort"); + + trace_actions(on->nested, on->nested_len, dp, &sctp_flow, + table_id, pipeline, &node->subs); +} + +static void +execute_sctp_abort(const struct ovnact_nest *on, + const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + bool loopback, enum ovnact_pipeline pipeline, + struct ovs_list *super) +{ + if (get_dl_type(uflow) == htons(ETH_TYPE_IP)) { + execute_sctp4_abort(on, dp, uflow, table_id, loopback, + pipeline, super); + } else { + execute_sctp6_abort(on, dp, uflow, table_id, loopback, + pipeline, super); + } +} + + static void execute_reject(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, @@ -1810,6 +1895,8 @@ execute_reject(const struct ovnact_nest *on, { if (uflow->nw_proto == IPPROTO_TCP) { execute_tcp_reset(on, dp, uflow, table_id, true, pipeline, super); + } else if (uflow->nw_proto == IPPROTO_SCTP) { + execute_sctp_abort(on, dp, uflow, table_id, true, pipeline, super); } else { if (get_dl_type(uflow) == htons(ETH_TYPE_IP)) { execute_icmp4(on, dp, uflow, table_id, true, pipeline, super); From patchwork Wed Dec 9 18:52:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1413619 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.138; helo=whitealder.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Gj5ReDwy; dkim-atps=neutral Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CrmQG197Rz9s0b for ; Thu, 10 Dec 2020 05:52:51 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 83E8786DCE; Wed, 9 Dec 2020 18:52:49 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id GxieUBTin+4k; Wed, 9 Dec 2020 18:52:47 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by whitealder.osuosl.org (Postfix) with ESMTP id 5CB1E86D7C; Wed, 9 Dec 2020 18:52:47 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 2C6DAC0FA7; Wed, 9 Dec 2020 18:52:47 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 369A7C013B for ; Wed, 9 Dec 2020 18:52:45 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 324E286D81 for ; Wed, 9 Dec 2020 18:52:45 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hNuX8T5DRKpp for ; Wed, 9 Dec 2020 18:52:44 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by whitealder.osuosl.org (Postfix) with ESMTPS id CABA986D7C for ; Wed, 9 Dec 2020 18:52:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1607539962; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wQWSet0m5pECjYgRRLuXC6o01pycuk19VA8p/oSkoIM=; b=Gj5ReDwyL1SWWVs6UHWtLLx7C55jaW3hFTSqZ8JJ+q3eCODO1lLi90cKVW1qISjaF5fn+e Rpe8qf1iw4SGh3BKxlJxCyMefUEIuOAnx1V/LWB9eYCUhOBsQvFx6CZzX4n+xJL4soOaIl aXgVWw7G2NZJ2dxv/Fs9swe8D4Q327o= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-223-Rjl-KjxuM5yyyl_xUpNu5g-1; Wed, 09 Dec 2020 13:52:35 -0500 X-MC-Unique: Rjl-KjxuM5yyyl_xUpNu5g-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DE2E51060B76 for ; Wed, 9 Dec 2020 18:52:34 +0000 (UTC) Received: from monae.redhat.com (ovpn-115-14.rdu2.redhat.com [10.10.115.14]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2D34F5FCB2 for ; Wed, 9 Dec 2020 18:52:34 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Wed, 9 Dec 2020 13:52:31 -0500 Message-Id: <20201209185231.557227-2-mmichels@redhat.com> In-Reply-To: <20201209185231.557227-1-mmichels@redhat.com> References: <20201209185231.557227-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn 2/2] Add sctp_abort logical flow action. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" 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 --- 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_ 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;