From: Zongkai LI <zealo...@gmail.com> This patch adds a new logical flow table to support Router Advertisement responder. And it also adds flow support for SLAAC. --- ovn/northd/ovn-northd.c | 97 +++++++++++++++++++++++++++++++++++++--- ovn/ovn-nb.ovsschema | 5 ++- ovn/ovn-nb.xml | 11 +++++ tests/ovn.at | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 8 deletions(-)
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 0874a9c..5c7806f 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -123,9 +123,10 @@ enum ovn_stage { PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \ PIPELINE_STAGE(ROUTER, IN, UNSNAT, 2, "lr_in_unsnat") \ PIPELINE_STAGE(ROUTER, IN, DNAT, 3, "lr_in_dnat") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 4, "lr_in_ip_routing") \ - PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 5, "lr_in_arp_resolve") \ - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 6, "lr_in_arp_request") \ + PIPELINE_STAGE(ROUTER, IN, RS_RSP, 4, "lr_in_rs_rsp") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 5, "lr_in_ip_routing") \ + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 6, "lr_in_arp_resolve") \ + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 7, "lr_in_arp_request") \ \ /* Logical router egress stages. */ \ PIPELINE_STAGE(ROUTER, OUT, SNAT, 0, "lr_out_snat") \ @@ -2721,6 +2722,32 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;"); } + /* Ingress table 12: Destination lookup, router solicitation handling + * (priority 110). */ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbs) { + continue; + } + + if (!od->n_router_ports) { + continue; + } + + ds_clear(&actions); + for (size_t i = 0; i != od->n_router_ports; i++) { + op = od->router_ports[i]; + if (!op->lsp_addrs || !op->lsp_addrs->n_ipv6_addrs) { + continue; + } + ds_put_format(&actions, "outport = %s; output; ", op->json_key); + } + if (actions.length != 0) { + ds_chomp(&actions, ' '); + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 110, "nd_rs", + ds_cstr(&actions)); + } + } + /* Ingress table 12: Destination lookup, broadcast and multicast handling * (priority 100). */ HMAP_FOR_EACH (op, key_node, ports) { @@ -3548,7 +3575,65 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "ip", "flags.loopback = 1; ct_dnat;"); } - /* Logical router ingress table 4: IP Routing. + /* Logical router ingress table 4: RS responder, by default goto next. + * (priority 0)*/ + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbr) { + continue; + } + + ovn_lflow_add(lflows, od, S_ROUTER_IN_RS_RSP, 0, "1", "next;"); + } + + /* Logical router ingress table 4: RS responder, reply for 'slaac' enabled + * router port. (priority 50)*/ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbrp || op->nbrp->peer + || !op->peer + || !op->nbrp->slaac + || !*op->nbrp->slaac) { + continue; + } + + ds_clear(&match); + ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && nd_rs", + op->json_key); + ds_clear(&actions); + const char *mtu_s = smap_get(&op->peer->od->nbs->other_config, "mtu"); + uint32_t mtu = (!mtu_s || atoi(mtu_s) < 1280) ? 1280 : atoi(mtu_s); + ds_put_format(&actions, "nd_ra{put_nd_ra(64,0,10800,0,0); " + "put_nd_opt_sll(%s); " + "put_nd_opt_mtu(%u); ", + op->lrp_networks.ea_s, + mtu); + size_t actions_len = actions.length; + for (size_t i = 0; i != op->lrp_networks.n_ipv6_addrs; i++) { + if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].network)) { + continue; + } + ds_put_format(&actions, + "put_nd_opt_prefix(%u,%u,10800,10800,%s); ", + op->lrp_networks.ipv6_addrs[i].plen, + (ND_PREFIX_ON_LINK | ND_PREFIX_AUTONOMOUS_ADDRESS), + op->lrp_networks.ipv6_addrs[i].network_s); + } + if (actions.length != actions_len) { + char ip6_str[INET6_ADDRSTRLEN + 1]; + struct in6_addr lla; + in6_generate_lla(op->lrp_networks.ea, &lla); + memset(ip6_str, 0, sizeof(ip6_str)); + ipv6_string_mapped(ip6_str, &lla); + ds_put_format(&actions, "eth.src = %s; ip6.src = %s; " + "outport = inport; flags.loopback = 1; " + "output;};", + op->lrp_networks.ea_s, + ip6_str); + ovn_lflow_add(lflows, op->od, S_ROUTER_IN_RS_RSP, 50, + ds_cstr(&match), ds_cstr(&actions)); + } + } + + /* Logical router ingress table 5: IP Routing. * * A packet that arrives at this table is an IP packet that should be * routed to the address in 'ip[46].dst'. This table sets outport to @@ -3590,7 +3675,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, /* XXX destination unreachable */ - /* Local router ingress table 5: ARP Resolution. + /* Local router ingress table 6: ARP Resolution. * * Any packet that reaches this table is an IP packet whose next-hop IP * address is in reg0. (ip4.dst is the final destination.) This table @@ -3787,7 +3872,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, "get_nd(outport, xxreg0); next;"); } - /* Local router ingress table 6: ARP request. + /* Local router ingress table 7: ARP request. * * In the common case where the Ethernet destination has been resolved, * this table outputs the packet (priority 0). Otherwise, it composes diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 456ae98..3c0361a 100644 --- a/ovn/ovn-nb.ovsschema +++ b/ovn/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "5.3.1", - "cksum": "1921908091 9353", + "version": "5.3.2", + "cksum": "1681625340 9428", "tables": { "NB_Global": { "columns": { @@ -154,6 +154,7 @@ "max": "unlimited"}}, "mac": {"type": "string"}, "peer": {"type": {"key": "string", "min": 0, "max": 1}}, + "slaac": {"type": {"key": "boolean", "min": 0, "max": 1}}, "enabled": {"type": {"key": "boolean", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 5719e74..5ed2709 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -132,6 +132,10 @@ column="addresses"/> column to request dynamic address assignment for a particular port. </column> + + <column name="other_config" key="mtu"> + MTU of logical switch. + </column> </group> <group title="Common Columns"> @@ -913,6 +917,13 @@ The Ethernet address that belongs to this router port. </column> + <column name="slaac"> + Setting <code>true</code> specifies the logical switch subnets behind + this router port IPv6 networks are going to use SLAAC as IPv6 address + configuration and RA mode. Otherwise set this column to + <code>false</code>, or this router port has no IPv6 networks. + </column> + <column name="enabled"> This column is used to administratively set port state. If this column is empty or is set to <code>true</code>, the port is enabled. If this diff --git a/tests/ovn.at b/tests/ovn.at index 3091131..6b4f72c 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -4983,3 +4983,117 @@ cat packets OVN_CLEANUP([hv1]) AT_CLEANUP + +AT_SETUP([ovn -- nd_ra ]) +AT_KEYWORDS([ovn-nd_ra]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) +ovn_start + +# In this test case we create 1 lswitch with 2 VIF ports attached, +# and a lrouter connected to the lswitch. +# Router solicitation packet we test, sent from VIF port, will be replied +# by local ovn-controller. + +# Create hypervisors and logical switch lsw0, logical router lr0, attach lsw0 +# onto lr0, set Logical_Router_Port.slaac column to 'true' to allow lrp0 send +# RA for SLAAC mode. +ovn-nbctl ls-add lsw0 +ovn-nbctl lr-add lr0 +ovn-nbctl lrp-add lr0 lrp0 fa:16:3e:00:00:01 fdad:1234:5678::1/64 +ovn-nbctl set Logical_Router_Port lrp0 slaac="true" +ovn-nbctl \ + -- lsp-add lsw0 lsp0 \ + -- set Logical_Switch_Port lsp0 type=router \ + options:router-port=lrp0 \ + addresses='"fa:16:3e:00:00:01 fdad:1234:5678::1"' +net_add n1 +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.2 + +# Add vif1 to hv1 and lsw0, turn on l2 port security on vif1. +ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap +ovn-nbctl lsp-add lsw0 lp1 +ovn-nbctl lsp-set-addresses lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2" +ovn-nbctl lsp-set-port-security lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2" + +# Add vif2 to hv1 and lsw0, turn on l2 port security on vif2. +ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap +ovn-nbctl lsp-add lsw0 lp2 +ovn-nbctl lsp-set-addresses lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3" +ovn-nbctl lsp-set-port-security lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3" + +# Add ACL rule for ICMPv6 on lsw0 +ovn-nbctl acl-add lsw0 from-lport 1002 'ip6 && icmp6' allow-related +ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp1" && ip6 && icmp6' allow-related +ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp2" && ip6 && icmp6' allow-related + +# Allow some time for ovn-northd and ovn-controller to catch up. +# XXX This should be more systematic. +sleep 1 + +# Given the name of a logical port, prints the name of the hypervisor +# on which it is located. +vif_to_hv() { + echo hv1${1%?} +} +trim_zeros() { + sed 's/\(00\)\{1,\}$//' +} +for i in 1 2; do + : > $i.expected +done + +# Complete Router Solicitation packet and Router Advertisement packet for vif1. +rs_packet=333300000002fa163e00000286dd6000000000103afffe80000000000000f8163efffe000002ff02000000000000000000000000000285000efc000000000101fa163e000002 +packet_l2=fa163e000002fa163e00000186dd +packet_l3=6000000000403afffe80000000000000f8163efffe000001fe80000000000000f8163efffe000002 +packet_l4_hd_no_csum=860040002a300000000000000000 +sll_opt=0101fa163e000001 +mtu_opt=0501000000000500 +prefix_opt=030440c000002a3000002a3000000000fdad1234567800000000000000000000 +ra_packet=$packet_l2$packet_l3$packet_l4_hd_no_csum$sll_opt$mtu_opt$prefix_opt + +as hv1 ovs-appctl netdev-dummy/receive vif1 $rs_packet +echo $ra_packet | trim_zeros >> 1.expected + +sleep 1 + +# Extend lrp0 networks, update lp2 addresses and l2 port security. +ovn-nbctl add Logical_Router_Port lrp0 networks \"fdad:9abc:def::1/64\" +ovn-nbctl set Logical_Switch_Port lp2 addresses \"fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3 fdad:9abc:def:0:f816:3eff:fe:3 +ovn-nbctl set Logical_Switch_Port lp2 port-security \"fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3 fdad:9abc:def:0:f816:3eff:fe:3 +# Update lsw0 other_config with mtu +ovn-nbctl add logical-switch lsw0 other_config mtu=1450 +# Update Router solicitation packet for lp2. +rs_packet=333300000002fa163e00000386dd6000000000103afffe80000000000000f8163efffe000003ff02000000000000000000000000000285000efa000000000101fa163e000003 +# Router Advertisement packet should have a new mtu option and a prefix information option. +packet_l2=fa163e000003fa163e00000186dd +packet_l3=6000000000603afffe80000000000000f8163efffe000001fe80000000000000f8163efffe000003 +mtu_opt=05010000000005aa +new_prefix_opt=030440c000002a3000002a3000000000fdad9abc0def00000000000000000000 +ra_packet=$packet_l2$packet_l3$packet_l4_hd_no_csum$sll_opt$mtu_opt$prefix_opt$new_prefix_opt + +as hv1 ovs-appctl netdev-dummy/receive vif2 $rs_packet +echo $ra_packet | trim_zeros >> 2.expected + +sleep 1 + +echo "------ hv1 dump ------" +as hv1 ovs-vsctl show +as hv1 ovs-ofctl -O OpenFlow13 show br-int +as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int + +for i in 1 2; do + file=hv1/vif$i-tx.pcap + echo $file + # Remove checksum to compare. + $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $file | trim_zeros | cut -b 1-112,117- > $i.packets + cat $i.expected > expout + AT_CHECK([cat $i.packets], [0], [expout]) +done + +OVN_CLEANUP([hv1]) + +AT_CLEANUP -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev