From patchwork Mon Oct 2 16:25:50 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 820622 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3y5SJt1KrXz9s7g for ; Tue, 3 Oct 2017 03:28:30 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 275C1B73; Mon, 2 Oct 2017 16:25:58 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id AB86BB65 for ; Mon, 2 Oct 2017 16:25:56 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 893851A9 for ; Mon, 2 Oct 2017 16:25:55 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 13A85806B3 for ; Mon, 2 Oct 2017 16:25:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 13A85806B3 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=nusiddiq@redhat.com Received: from numans.blr.redhat.com (dhcp-0-126.blr.redhat.com [10.70.1.126]) by smtp.corp.redhat.com (Postfix) with ESMTP id B22194D72A; Mon, 2 Oct 2017 16:25:53 +0000 (UTC) From: nusiddiq@redhat.com To: dev@openvswitch.org Date: Mon, 2 Oct 2017 21:55:50 +0530 Message-Id: <20171002162550.1923-1-nusiddiq@redhat.com> In-Reply-To: <20171002161109.956-1-nusiddiq@redhat.com> References: <20171002161109.956-1-nusiddiq@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Mon, 02 Oct 2017 16:25:55 +0000 (UTC) X-Spam-Status: No, score=-5.0 required=5.0 tests=RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=disabled version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v9 4/4] ovn: Generate Neighbor Solicitation packet for unknown MAC IPv6 packets X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org From: Numan Siddique In the router ingress pipeline, if the destination mac is unresolved by the time the packet reaches the ARP_REQUEST stage, OVN should generate an IPv6 Neighbor Solicitation packet to learn the MAC address. This feature is presently missing. This patch adds this feature. A new action "nd_ns" is added which replaces an IPv6 packet being processed with an IPv6 Neighbor Solicitation packet. ovn-northd adds a flow in the ARP_REQUEST router ingress pipeline stage if the eth.dst is zero which applies this action. This action is similar to the IPv4 counterpart "arp" action. OVN already has the support to learn the MAC from the IPv6 Neighbor Advertisement packets and storing in the south bound MAC_Binding table. Signed-off-by: Numan Siddique Acked-by: Mark Michelson --- include/ovn/actions.h | 9 +++- ovn/controller/pinctrl.c | 122 +++++++++++++++++++++++--------------------- ovn/lib/actions.c | 22 ++++++++ ovn/northd/ovn-northd.8.xml | 24 ++++++--- ovn/northd/ovn-northd.c | 8 ++- ovn/ovn-sb.xml | 37 ++++++++++++++ ovn/utilities/ovn-trace.c | 30 +++++++++++ tests/ovn.at | 116 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 302 insertions(+), 66 deletions(-) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 15cee478d..8c7208ffc 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -73,7 +73,8 @@ struct simap; OVNACT(SET_QUEUE, ovnact_set_queue) \ OVNACT(DNS_LOOKUP, ovnact_dns_lookup) \ OVNACT(LOG, ovnact_log) \ - OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) + OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) \ + OVNACT(ND_NS, ovnact_nest) /* enum ovnact_type, with a member OVNACT_ for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -427,6 +428,12 @@ enum action_opcode { * - Any number of ICMPv6 options. */ ACTION_OPCODE_PUT_ND_RA_OPTS, + + /* "nd_ns { ...actions... }". + * + * The actions, in OpenFlow 1.3 format, follow the action_header. + */ + ACTION_OPCODE_ND_NS, }; /* Header. */ diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 3a1348937..5aedf7d0d 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -85,6 +85,9 @@ static void pinctrl_handle_put_nd_ra_opts( const struct flow *ip_flow, struct dp_packet *pkt_in, struct ofputil_packet_in *pin, struct ofpbuf *userdata, struct ofpbuf *continuation); +static void pinctrl_handle_nd_ns(const struct flow *ip_flow, + const struct match *md, + struct ofpbuf *userdata); COVERAGE_DEFINE(pinctrl_drop_put_mac_binding); @@ -132,6 +135,43 @@ set_switch_config(struct rconn *swconn, } static void +set_actions_and_enqueue_msg(const struct dp_packet *packet, + const struct match *md, + struct ofpbuf *userdata) +{ + /* Copy metadata from 'md' into the packet-out via "set_field" + * actions, then add actions from 'userdata'. + */ + uint64_t ofpacts_stub[4096 / 8]; + struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); + enum ofp_version version = rconn_get_version(swconn); + + reload_metadata(&ofpacts, md); + enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, + version, NULL, NULL, + &ofpacts); + if (error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "failed to parse actions from userdata (%s)", + ofperr_to_string(error)); + ofpbuf_uninit(&ofpacts); + return; + } + + struct ofputil_packet_out po = { + .packet = dp_packet_data(packet), + .packet_len = dp_packet_size(packet), + .buffer_id = UINT32_MAX, + .ofpacts = ofpacts.data, + .ofpacts_len = ofpacts.size, + }; + match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); + queue_msg(ofputil_encode_packet_out(&po, proto)); + ofpbuf_uninit(&ofpacts); +} + +static void pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md, struct ofpbuf *userdata) { @@ -166,40 +206,8 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md, ip_flow->vlans[0].tci); } - /* Compose actions. - * - * First, copy metadata from 'md' into the packet-out via "set_field" - * actions, then add actions from 'userdata'. - */ - uint64_t ofpacts_stub[4096 / 8]; - struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); - enum ofp_version version = rconn_get_version(swconn); - - reload_metadata(&ofpacts, md); - enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, - version, NULL, NULL, - &ofpacts); - if (error) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "failed to parse arp actions (%s)", - ofperr_to_string(error)); - goto exit; - } - - struct ofputil_packet_out po = { - .packet = dp_packet_data(&packet), - .packet_len = dp_packet_size(&packet), - .buffer_id = UINT32_MAX, - .ofpacts = ofpacts.data, - .ofpacts_len = ofpacts.size, - }; - match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); - enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); - queue_msg(ofputil_encode_packet_out(&po, proto)); - -exit: + set_actions_and_enqueue_msg(&packet, md, userdata); dp_packet_uninit(&packet); - ofpbuf_uninit(&ofpacts); } static void @@ -994,6 +1002,10 @@ process_packet_in(const struct ofp_header *msg, struct controller_ctx *ctx) &continuation); break; + case ACTION_OPCODE_ND_NS: + pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata); + break; + default: VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, ntohl(ah->opcode)); @@ -1812,9 +1824,6 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md, return; } - enum ofp_version version = rconn_get_version(swconn); - enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); - uint64_t packet_stub[128 / 8]; struct dp_packet packet; dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); @@ -1827,35 +1836,32 @@ pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md, &ip_flow->nd_target, &ip_flow->ipv6_src, htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE)); - /* Reload previous packet metadata. */ - uint64_t ofpacts_stub[4096 / 8]; - struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); - reload_metadata(&ofpacts, md); + /* Reload previous packet metadata and set actions from userdata. */ + set_actions_and_enqueue_msg(&packet, md, userdata); + dp_packet_uninit(&packet); +} - enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, - version, NULL, NULL, - &ofpacts); - if (error) { +static void +pinctrl_handle_nd_ns(const struct flow *ip_flow, const struct match *md, + struct ofpbuf *userdata) +{ + /* This action only works for IPv6 packets. */ + if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - VLOG_WARN_RL(&rl, "failed to parse actions for 'na' (%s)", - ofperr_to_string(error)); - goto exit; + VLOG_WARN_RL(&rl, "NS action on non-IPv6 packet"); + return; } - struct ofputil_packet_out po = { - .packet = dp_packet_data(&packet), - .packet_len = dp_packet_size(&packet), - .buffer_id = UINT32_MAX, - .ofpacts = ofpacts.data, - .ofpacts_len = ofpacts.size, - }; - match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER); + uint64_t packet_stub[128 / 8]; + struct dp_packet packet; + dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); - queue_msg(ofputil_encode_packet_out(&po, proto)); + compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src, + &ip_flow->ipv6_dst); -exit: + /* Reload previous packet metadata and set actions from userdata. */ + set_actions_and_enqueue_msg(&packet, md, userdata); dp_packet_uninit(&packet); - ofpbuf_uninit(&ofpacts); } static void diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index 8d5863c0a..d0a4d7753 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -1134,6 +1134,12 @@ parse_ND_NA(struct action_context *ctx) } static void +parse_ND_NS(struct action_context *ctx) +{ + parse_nested_action(ctx, OVNACT_ND_NS, "ip6"); +} + +static void parse_CLONE(struct action_context *ctx) { parse_nested_action(ctx, OVNACT_CLONE, NULL); @@ -1161,6 +1167,12 @@ format_ND_NA(const struct ovnact_nest *nest, struct ds *s) } static void +format_ND_NS(const struct ovnact_nest *nest, struct ds *s) +{ + format_nested_action(nest, "nd_ns", s); +} + +static void format_CLONE(const struct ovnact_nest *nest, struct ds *s) { format_nested_action(nest, "clone", s); @@ -1207,6 +1219,14 @@ encode_ND_NA(const struct ovnact_nest *on, } static void +encode_ND_NS(const struct ovnact_nest *on, + const struct ovnact_encode_params *ep, + struct ofpbuf *ofpacts) +{ + encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts); +} + +static void encode_CLONE(const struct ovnact_nest *on, const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts) @@ -2146,6 +2166,8 @@ parse_action(struct action_context *ctx) parse_ARP(ctx); } else if (lexer_match_id(ctx->lexer, "nd_na")) { parse_ND_NA(ctx); + } else if (lexer_match_id(ctx->lexer, "nd_ns")) { + parse_ND_NS(ctx); } else if (lexer_match_id(ctx->lexer, "get_arp")) { parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts)); } else if (lexer_match_id(ctx->lexer, "put_arp")) { diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index a994abf78..17123c690 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -1915,15 +1915,15 @@ next;

In the common case where the Ethernet destination has been resolved, this - table outputs the packet. Otherwise, it composes and sends an ARP - request. It holds the following flows: + table outputs the packet. Otherwise, it composes and sends an ARP or + IPv6 Neighbor Solicitation request. It holds the following flows:

  • - Unknown MAC address. A priority-100 flow with match eth.dst == - 00:00:00:00:00:00 has the following actions: + Unknown MAC address. A priority-100 flow for IPv4 packets with match + eth.dst == 00:00:00:00:00:00 has the following actions:

    @@ -1937,13 +1937,25 @@ arp {
             

    + Unknown MAC address. A priority-100 flow for IPv6 packets with match + eth.dst == 00:00:00:00:00:00 has the following actions: +

    + +
    +nd_ns {
    +    nd.target = xxreg0;
    +    output;
    +};
    +        
    + +

    (Ingress table IP Routing initialized reg1 with the IP address owned by outport and - reg0 with the next-hop IP address) + (xx)reg0 with the next-hop IP address)

    - The IP packet that triggers the ARP request is dropped. + The IP packet that triggers the ARP/IPv6 NS request is dropped.

  • diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 3da20d25b..b4ea34bc6 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -5703,7 +5703,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, * * In the common case where the Ethernet destination has been resolved, * this table outputs the packet (priority 0). Otherwise, it composes - * and sends an ARP request (priority 100). */ + * and sends an ARP/IPv6 NA request (priority 100). */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbr) { continue; @@ -5718,6 +5718,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "arp.op = 1; " /* ARP request */ "output; " "};"); + ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100, + "eth.dst == 00:00:00:00:00:00", + "nd_ns { " + "nd.target = xxreg0; " + "output; " + "};"); ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;"); } diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml index 2e4f28b96..ca8cbecdd 100644 --- a/ovn/ovn-sb.xml +++ b/ovn/ovn-sb.xml @@ -1258,6 +1258,43 @@

    Example: put_arp(inport, arp.spa, arp.sha);

    +
    nd_ns { action; ... };
    +
    +

    + Temporarily replaces the IPv6 packet being processed by an IPv6 + Neighbor Solicitation packet and executes each nested + action on the IPv6 NS packet. Actions following the + nd_ns action, if any, apply to the original, unmodified + packet. +

    + +

    + The IPv6 NS packet that this action operates on is initialized + based on the IPv6 packet being processed, as follows. These are + default values that the nested actions will probably want to + change: +

    + +
      +
    • eth.src unchanged
    • +
    • eth.dst set to IPv6 multicast MAC address
    • +
    • eth.type = 0x86dd
    • +
    • ip6.src copied from ip6.src
    • +
    • + ip6.dst set to IPv6 Solicited-Node multicast address +
    • +
    • icmp6.type = 135 (Neighbor Solicitation)
    • +
    • nd.target copied from ip6.dst
    • +
    + +

    + The IPv6 NS packet has the same VLAN header, if any, as the IP + packet it replaces. +

    + +

    Prerequisite: ip6

    +
    +
    nd_na { action; ... };
    diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c index 211148b8b..e457284fc 100644 --- a/ovn/utilities/ovn-trace.c +++ b/ovn/utilities/ovn-trace.c @@ -1510,6 +1510,31 @@ execute_nd_na(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, } static void +execute_nd_ns(const struct ovnact_nest *on, const struct ovntrace_datapath *dp, + const struct flow *uflow, uint8_t table_id, + enum ovnact_pipeline pipeline, struct ovs_list *super) +{ + struct flow na_flow = *uflow; + + /* Update fields for NA. */ + na_flow.dl_src = uflow->dl_src; + na_flow.ipv6_src = uflow->ipv6_src; + na_flow.ipv6_dst = uflow->ipv6_dst; + struct in6_addr sn_addr; + in6_addr_solicited_node(&sn_addr, &uflow->ipv6_dst); + ipv6_multicast_to_ethernet(&na_flow.dl_dst, &sn_addr); + na_flow.tp_src = htons(135); + na_flow.arp_sha = eth_addr_zero; + na_flow.arp_tha = uflow->dl_dst; + + struct ovntrace_node *node = ovntrace_node_append( + super, OVNTRACE_NODE_TRANSFORMATION, "nd_ns"); + + trace_actions(on->nested, on->nested_len, dp, &na_flow, + table_id, pipeline, &node->subs); +} + +static void execute_get_mac_bind(const struct ovnact_get_mac_bind *bind, const struct ovntrace_datapath *dp, struct flow *uflow, struct ovs_list *super) @@ -1811,6 +1836,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, super); break; + case OVNACT_ND_NS: + execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id, pipeline, + super); + break; + case OVNACT_GET_ARP: execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super); break; diff --git a/tests/ovn.at b/tests/ovn.at index 3aa4e5e22..13cdc1679 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -993,6 +993,16 @@ reg1[0] = put_dhcp_opts(offerip="xyzzy"); reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4); DHCPv4 option domain requires string value. +# nd_ns +nd_ns { nd.target = xxreg0; output; }; + encodes as controller(userdata=00.00.00.09.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.80.00.00.00.00.00.01.de.10.00.01.2e.10.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00) + has prereqs ip6 + +nd_ns { }; + formats as nd_ns { drop; }; + encodes as controller(userdata=00.00.00.09.00.00.00.00) + has prereqs ip6 + # nd_na nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; }; formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; }; @@ -8795,6 +8805,112 @@ OVN_CLEANUP([gw1],[gw2],[hv1]) AT_CLEANUP +AT_SETUP([ovn -- IPv6 Neighbor Solicitation for unknown MAC]) +AT_KEYWORDS([ovn-nd_ns for unknown mac]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +ovn-nbctl ls-add sw0_ip6 +ovn-nbctl lsp-add sw0_ip6 sw0_ip6-port1 +ovn-nbctl lsp-set-addresses sw0_ip6-port1 \ +"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002" + +ovn-nbctl lsp-set-port-security sw0_ip6-port1 \ +"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002" + +ovn-nbctl lr-add lr0_ip6 +ovn-nbctl lrp-add lr0_ip6 lrp0_ip6 00:00:00:00:af:01 aef0::/64 +ovn-nbctl lsp-add sw0_ip6 lrp0_ip6-attachment +ovn-nbctl lsp-set-type lrp0_ip6-attachment router +ovn-nbctl lsp-set-addresses lrp0_ip6-attachment 00:00:00:00:af:01 +ovn-nbctl lsp-set-options lrp0_ip6-attachment router-port=lrp0_ip6 +ovn-nbctl set logical_router_port lrp0_ip6 ipv6_ra_configs:address_mode=slaac + +ovn-nbctl ls-add public +ovn-nbctl lsp-add public ln-public +ovn-nbctl lsp-set-addresses ln-public unknown +ovn-nbctl lsp-set-type ln-public localnet +ovn-nbctl lsp-set-options ln-public network_name=phys + +ovn-nbctl lrp-add lr0_ip6 ip6_public 00:00:02:01:02:04 \ +2001:db8:1:0:200:02ff:fe01:0204/64 \ +-- set Logical_Router_port ip6_public options:redirect-chassis="hv1" + + +ovn-nbctl lsp-add public rp-ip6_public -- set Logical_Switch_Port \ +rp-ip6_public type=router options:router-port=ip6_public \ +-- lsp-set-addresses rp-ip6_public router + +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 + +ovs-vsctl -- add-port br-int hv1-vif1 -- \ + set interface hv1-vif1 external-ids:iface-id=sw0_ip6-port1 \ + options:tx_pcap=hv1/vif1-tx.pcap \ + options:rxq_pcap=hv1/vif1-rx.pcap \ + ofport-request=1 +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys + +# Allow some time for ovn-northd and ovn-controller to catch up. +# XXX This should be more systematic. +sleep 1 + +trim_zeros() { + sed 's/\(00\)\{1,\}$//' +} + +# Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC +# addresses. ovn-controller should generate an IPv6 NS request for IPv6 +# packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage. +# test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT... +# This function sends ipv6 packet +test_ipv6() { + local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 + dst_ip=20010db800010000020002fffe010205 + + local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip} + packet=${packet}8000000000000000 + shift; shift; shift; shift + + dst_mac=3333ff010205 + src_mac=000002010204 + mcast_node_ip=ff0200000000000000000001ff010205 + expected_packet=${dst_mac}${src_mac}86dd6000000000203aff${src_ip} + expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000${dst_ip} + expected_packet=${expected_packet}0101${src_mac} + + as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $packet + echo $expected_packet >> ipv6_ns.expected +} + +src_mac=506400000002 +dst_mac=00000000af01 +src_ip=aef0000000000000526400fffe000002 +# Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet +# should be received by the ports attached to br-phys. +test_ipv6 1 $src_mac $dst_mac $src_ip 2 + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap | \ +trim_zeros > 1.packets +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | \ +trim_zeros > 2.packets + +cat ipv6_ns.expected | cut -c -112 > expout +AT_CHECK([cat 1.packets | cut -c -112], [0], [expout]) +AT_CHECK([cat 2.packets | cut -c -112], [0], [expout]) + +# Skipping the ICMPv6 checksum +cat ipv6_ns.expected | cut -c 117- > expout +AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout]) +AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout]) + +OVN_CLEANUP([hv1]) + +AT_CLEANUP + AT_SETUP([ovn -- options:requested-chassis for logical port]) ovn_start