On Thu, Sep 28, 2017 at 2:12 AM, Mark Michelson <mmich...@redhat.com> wrote:
> > > On Thu, Sep 21, 2017 at 11:12 AM <nusid...@redhat.com> wrote: > >> From: Numan Siddique <nusid...@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 <nusid...@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 6dbea4efa..cad0f67ba 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 OVS_UNUSED); >> +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_enqueu_msg(const struct dp_packet *packet, >> > > Nit: The word is spelled "enqueue", not "enqueu". This could potentially > lead to some confusion if left this way. > Thanks Mark for the comments. I will take care of this in the next patch set. I will wait for a while so that I can incorporate review comments (if any) from the other patches in this series. > >> + 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_enqueu_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_enqueu_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_enqueu_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 2981d89f6..5e5c666a9 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.3 >> >> _______________________________________________ >> dev mailing list >> d...@openvswitch.org >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >> > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev