[ovs-dev] [PATCH v9 4/4] ovn: Generate Neighbor Solicitation packet for unknown MAC IPv6 packets
Mark Michelson
mmichels at redhat.com
Wed Oct 4 18:01:48 UTC 2017
On Mon, Oct 2, 2017 at 11:28 AM <nusiddiq at redhat.com> wrote:
> From: Numan Siddique <nusiddiq at redhat.com>
>
> 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 <nusiddiq at redhat.com>
>
Acked-by: Mark Michelson <mmichels at redhat.com>
> ---
> 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_<ENUM> 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;
>
> <p>
> 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:
> </p>
>
> <ul>
> <li>
> <p>
> - Unknown MAC address. A priority-100 flow with match
> <code>eth.dst ==
> - 00:00:00:00:00:00</code> has the following actions:
> + Unknown MAC address. A priority-100 flow for IPv4 packets with
> match
> + <code>eth.dst == 00:00:00:00:00:00</code> has the following
> actions:
> </p>
>
> <pre>
> @@ -1937,13 +1937,25 @@ arp {
> </pre>
>
> <p>
> + Unknown MAC address. A priority-100 flow for IPv6 packets with
> match
> + <code>eth.dst == 00:00:00:00:00:00</code> has the following
> actions:
> + </p>
> +
> + <pre>
> +nd_ns {
> + nd.target = xxreg0;
> + output;
> +};
> + </pre>
> +
> + <p>
> (Ingress table <code>IP Routing</code> initialized
> <code>reg1</code>
> with the IP address owned by <code>outport</code> and
> - <code>reg0</code> with the next-hop IP address)
> + <code>(xx)reg0</code> with the next-hop IP address)
> </p>
>
> <p>
> - The IP packet that triggers the ARP request is dropped.
> + The IP packet that triggers the ARP/IPv6 NS request is dropped.
> </p>
> </li>
>
> 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 @@
> <p><b>Example:</b> <code>put_arp(inport, arp.spa,
> arp.sha);</code></p>
> </dd>
>
> + <dt><code>nd_ns { <var>action</var>; </code>...<code>
> };</code></dt>
> + <dd>
> + <p>
> + Temporarily replaces the IPv6 packet being processed by an
> IPv6
> + Neighbor Solicitation packet and executes each nested
> + <var>action</var> on the IPv6 NS packet. Actions following
> the
> + <var>nd_ns</var> action, if any, apply to the original,
> unmodified
> + packet.
> + </p>
> +
> + <p>
> + 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:
> + </p>
> +
> + <ul>
> + <li><code>eth.src</code> unchanged</li>
> + <li><code>eth.dst</code> set to IPv6 multicast MAC
> address</li>
> + <li><code>eth.type = 0x86dd</code></li>
> + <li><code>ip6.src</code> copied from <code>ip6.src</code></li>
> + <li>
> + <code>ip6.dst</code> set to IPv6 Solicited-Node multicast
> address
> + </li>
> + <li><code>icmp6.type = 135</code> (Neighbor Solicitation)</li>
> + <li><code>nd.target</code> copied from
> <code>ip6.dst</code></li>
> + </ul>
> +
> + <p>
> + The IPv6 NS packet has the same VLAN header, if any, as the IP
> + packet it replaces.
> + </p>
> +
> + <p><b>Prerequisite:</b> <code>ip6</code></p>
> + </dd>
> +
> <dt>
> <code>nd_na { <var>action</var>; </code>...<code> };</code>
> </dt>
> 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
>
> --
> 2.13.5
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
More information about the dev
mailing list