This patch adds default_dhcpv4_options and default_dhcpv6_options columns for Logical_Switch, which should help CMS not to calculate and set dhcpv4_options and dhcpv6_options columns for lswitch ports on lswitchs one by one, when most of lswitch ports on the same lswitch are using the DHCPv4_Options and DHCPv6_Options. Default DHCP(v4 and v6) Options should benefit in case scalling up and DB synchronization between CMS and OVN NB.
v1 -> v2 add ACL lflows support for lswitch ports using default_dhcpvx_options from lswitch. v2 -> v3 update ovn dhcpv4 and dhcpv6 tests for lswitch ports using specific dhcp options than defualt ones in lswitch. --- ovn/northd/ovn-northd.c | 144 ++++++++++++++++++++++++++++++++++++------------ ovn/ovn-nb.ovsschema | 18 +++++- ovn/ovn-nb.xml | 26 +++++++++ tests/ovn.at | 74 +++++++++++++++++++++++-- 4 files changed, 220 insertions(+), 42 deletions(-) diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 0ad9190..3ab11fa 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -1784,14 +1784,27 @@ static bool build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, struct ds *options_action, struct ds *response_action) { + char *cidr = NULL; + struct smap dhcpv4_options = SMAP_INITIALIZER(&dhcpv4_options); if (!op->nbsp->dhcpv4_options) { - /* CMS has disabled native DHCPv4 for this lport. */ - return false; + if ((!op->nbsp->use_default_dhcpv4 || *op->nbsp->use_default_dhcpv4) + && op->od->nbs->default_dhcpv4_options) { + /* Use lswitch default DHCPv4 options for this lport. */ + cidr = op->od->nbs->default_dhcpv4_options->cidr; + smap_clone(&dhcpv4_options, + &op->od->nbs->default_dhcpv4_options->options); + } else { + /* CMS has disabled native DHCPv4 for this lport. */ + smap_destroy(&dhcpv4_options); + return false; + } + } else { + cidr = op->nbsp->dhcpv4_options->cidr; + smap_clone(&dhcpv4_options, &op->nbsp->dhcpv4_options->options); } ovs_be32 host_ip, mask; - char *error = ip_parse_masked(op->nbsp->dhcpv4_options->cidr, &host_ip, - &mask); + char *error = ip_parse_masked(cidr, &host_ip, &mask); if (error || ((offer_ip ^ host_ip) & mask)) { /* Either * - cidr defined is invalid or @@ -1802,14 +1815,10 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, return false; } - const char *server_ip = smap_get( - &op->nbsp->dhcpv4_options->options, "server_id"); - const char *server_mac = smap_get( - &op->nbsp->dhcpv4_options->options, "server_mac"); - const char *lease_time = smap_get( - &op->nbsp->dhcpv4_options->options, "lease_time"); - const char *router = smap_get( - &op->nbsp->dhcpv4_options->options, "router"); + const char *server_ip = smap_get(&dhcpv4_options, "server_id"); + const char *server_mac = smap_get(&dhcpv4_options, "server_mac"); + const char *lease_time = smap_get(&dhcpv4_options, "lease_time"); + const char *router = smap_get(&dhcpv4_options, "router"); if (!(server_ip && server_mac && lease_time && router)) { /* "server_id", "server_mac", "lease_time" and "router" should be @@ -1820,8 +1829,8 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, return false; } - struct smap dhcpv4_options = SMAP_INITIALIZER(&dhcpv4_options); - smap_clone(&dhcpv4_options, &op->nbsp->dhcpv4_options->options); + char server_mac_str[ETH_ADDR_STRLEN + 1]; + strcpy(server_mac_str, server_mac); /* server_mac is not DHCPv4 option, delete it from the smap. */ smap_remove(&dhcpv4_options, "server_mac"); @@ -1845,7 +1854,7 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; " "udp.dst = 68; outport = inport; flags.loopback = 1; " "output;", - server_mac, IP_ARGS(offer_ip), server_ip); + server_mac_str, IP_ARGS(offer_ip), server_ip); smap_destroy(&dhcpv4_options); return true; @@ -1855,15 +1864,28 @@ static bool build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip, struct ds *options_action, struct ds *response_action) { + char *cidr = NULL; + struct smap dhcpv6_options = SMAP_INITIALIZER(&dhcpv6_options); if (!op->nbsp->dhcpv6_options) { - /* CMS has disabled native DHCPv6 for this lport. */ - return false; + if ((!op->nbsp->use_default_dhcpv6 || *op->nbsp->use_default_dhcpv6) + && op->od->nbs->default_dhcpv6_options) { + /* Use lswitch default DHCPv6 options for this lport. */ + cidr = op->od->nbs->default_dhcpv6_options->cidr; + smap_clone(&dhcpv6_options, + &op->od->nbs->default_dhcpv6_options->options); + } else { + /* CMS has disabled native DHCPv6 for this lport. */ + smap_destroy(&dhcpv6_options); + return false; + } + } else { + cidr = op->nbsp->dhcpv6_options->cidr; + smap_clone(&dhcpv6_options, &op->nbsp->dhcpv6_options->options); } struct in6_addr host_ip, mask; - char *error = ipv6_parse_masked(op->nbsp->dhcpv6_options->cidr, &host_ip, - &mask); + char *error = ipv6_parse_masked(cidr, &host_ip, &mask); if (error) { free(error); return false; @@ -1877,8 +1899,7 @@ build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip, } /* "server_id" should be the MAC address. */ - const char *server_mac = smap_get(&op->nbsp->dhcpv6_options->options, - "server_id"); + const char *server_mac = smap_get(&dhcpv6_options, "server_id"); struct eth_addr ea; if (!server_mac || !eth_addr_from_string(server_mac, &ea)) { /* "server_id" should be present in the dhcpv6_options. */ @@ -1902,7 +1923,7 @@ build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip, REGBIT_DHCP_OPTS_RESULT" = put_dhcpv6_opts(ia_addr = %s, ", ia_addr); struct smap_node *node; - SMAP_FOR_EACH (node, &op->nbsp->dhcpv6_options->options) { + SMAP_FOR_EACH (node, &dhcpv6_options) { ds_put_format(options_action, "%s = %s, ", node->key, node->value); } ds_chomp(options_action, ' '); @@ -2321,6 +2342,39 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows) /* Add 34000 priority flow to allow DHCP reply from ovn-controller to all * logical ports of the datapath if the CMS has configured DHCPv4 options*/ + struct ds od_default_dhcpv4_match = DS_EMPTY_INITIALIZER; + if (od->nbs->default_dhcpv4_options) { + const char *server_id = smap_get( + &od->nbs->default_dhcpv4_options->options, "server_id"); + const char *server_mac = smap_get( + &od->nbs->default_dhcpv4_options->options, "server_mac"); + const char *lease_time = smap_get( + &od->nbs->default_dhcpv4_options->options, "lease_time"); + const char *router = smap_get( + &od->nbs->default_dhcpv4_options->options, "router"); + if (server_id && server_mac && lease_time && router) { + ds_put_format(&od_default_dhcpv4_match, "eth.src == %s " + "&& ip4.src == %s && udp && udp.src == 67 " + "&& udp.dst == 68", + server_mac, server_id); + } + } + struct ds od_default_dhcpv6_match = DS_EMPTY_INITIALIZER; + if (od->nbs->default_dhcpv6_options) { + const char *server_mac = smap_get( + &od->nbs->default_dhcpv6_options->options, "server_id"); + struct eth_addr ea; + if (server_mac && eth_addr_from_string(server_mac, &ea)) { + struct in6_addr lla; + in6_generate_lla(ea, &lla); + char server_ip[INET6_ADDRSTRLEN + 1]; + ipv6_string_mapped(server_ip, &lla); + ds_put_format(&od_default_dhcpv6_match, "eth.src == %s " + "&& ip6.src == %s && udp && udp.src == 547 " + "&& udp.dst == 546", + server_mac, server_ip); + } + } for (size_t i = 0; i < od->nbs->n_ports; i++) { if (od->nbs->ports[i]->dhcpv4_options) { const char *server_id = smap_get( @@ -2343,6 +2397,18 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows) lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), actions); } + } else if (od_default_dhcpv4_match.length != 0 + && (!od->nbs->ports[i]->use_default_dhcpv4 + || *od->nbs->ports[i]->use_default_dhcpv4)) { + struct ds match = DS_EMPTY_INITIALIZER; + const char *actions = + has_stateful ? "ct_commit; next;" : "next;"; + ds_put_format(&match, "outport == \"%s\" && %s", + od->nbs->ports[i]->name, + ds_cstr(&od_default_dhcpv4_match)); + ovn_lflow_add( + lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), + actions); } if (od->nbs->ports[i]->dhcpv6_options) { @@ -2369,6 +2435,18 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows) lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), actions); } + } else if (od_default_dhcpv6_match.length != 0 + && (!od->nbs->ports[i]->use_default_dhcpv6 + || *od->nbs->ports[i]->use_default_dhcpv6)) { + struct ds match = DS_EMPTY_INITIALIZER; + const char *actions = + has_stateful ? "ct_commit; next;" : "next;"; + ds_put_format(&match, "outport == \"%s\" && %s", + od->nbs->ports[i]->name, + ds_cstr(&od_default_dhcpv6_match)); + ovn_lflow_add( + lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), + actions); } } } @@ -2661,16 +2739,12 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, continue; } - if (!op->nbsp->dhcpv4_options && !op->nbsp->dhcpv6_options) { - /* CMS has disabled both native DHCPv4 and DHCPv6 for this lport. - */ - continue; - } - + struct ds options_action = DS_EMPTY_INITIALIZER; + struct ds response_action = DS_EMPTY_INITIALIZER; for (size_t i = 0; i < op->n_lsp_addrs; i++) { + ds_clear(&options_action); + ds_clear(&response_action); for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) { - struct ds options_action = DS_EMPTY_INITIALIZER; - struct ds response_action = DS_EMPTY_INITIALIZER; if (build_dhcpv4_action( op, op->lsp_addrs[i].ipv4_addrs[j].addr, &options_action, &response_action)) { @@ -2691,15 +2765,13 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, 100, ds_cstr(&match), ds_cstr(&response_action)); ds_destroy(&match); - ds_destroy(&options_action); - ds_destroy(&response_action); break; } } + ds_clear(&options_action); + ds_clear(&response_action); for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) { - struct ds options_action = DS_EMPTY_INITIALIZER; - struct ds response_action = DS_EMPTY_INITIALIZER; if (build_dhcpv6_action( op, &op->lsp_addrs[i].ipv6_addrs[j].addr, &options_action, &response_action)) { @@ -2719,12 +2791,12 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100, ds_cstr(&match), ds_cstr(&response_action)); ds_destroy(&match); - ds_destroy(&options_action); - ds_destroy(&response_action); break; } } } + ds_destroy(&options_action); + ds_destroy(&response_action); } /* Ingress table 10 and 11: DHCP options and response, by default goto next. diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 456ae98..0591065 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.4.0", + "cksum": "2099971381 10299", "tables": { "NB_Global": { "columns": { @@ -31,6 +31,16 @@ "refType": "strong"}, "min": 0, "max": "unlimited"}}, + "default_dhcpv4_options": {"type": {"key": {"type": "uuid", + "refTable": "DHCP_Options", + "refType": "weak"}, + "min": 0, + "max": 1}}, + "default_dhcpv6_options": {"type": {"key": {"type": "uuid", + "refTable": "DHCP_Options", + "refType": "weak"}, + "min": 0, + "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, @@ -74,6 +84,10 @@ "refType": "weak"}, "min": 0, "max": 1}}, + "use_default_dhcpv4": {"type": {"key": "boolean", + "min": 0, "max": 1}}, + "use_default_dhcpv6": {"type": {"key": "boolean", + "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 5719e74..37b4481 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -119,6 +119,18 @@ Access control rules that apply to packets within the logical switch. </column> + <column name="default_dhcpv4_options"> + This column defines the DHCPv4 Options would used by ports on switch. + Check column <ref column="use_default_dhcpv4"/> in table + <ref table="Logical_Switch_Port"/>. + </column> + + <column name="default_dhcpv6_options"> + This column defines the DHCPv6 Options would used by ports on switch. + Check column <ref column="use_default_dhcpv6"/> in table + <ref table="Logical_Switch_Port"/>. + </column> + <group title="other_config"> <p> Additional configuration options for the logical switch. @@ -609,6 +621,20 @@ Please see the <ref table="DHCP_Options"/> table. </column> + <column name="use_default_dhcpv4"> + This column defines whether the port should use DHCPv4 Options owned by + lswitch. Only when column <ref column="dhcpv4_options"/> is unset or + empty, and this column is set to true, port will use DHCPv4 Options + owned by lswitch. Default to true. + </column> + + <column name="use_default_dhcpv6"> + This column defines whether the port should use DHCPv6 Options owned by + lswitch. Only when column <ref column="dhcpv6_options"/> is unset or + empty, and this column is set to true, port will use DHCPv6 Options + owned by lswitch. Default to true. + </column> + <column name="external_ids"> See <em>External IDs</em> at the beginning of this document. </column> diff --git a/tests/ovn.at b/tests/ovn.at index a23b422..bc12179 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -3491,6 +3491,11 @@ ovn-nbctl lsp-add ls1 ls1-lp2 \ ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" +ovn-nbctl lsp-add ls1 ls1-lp3 \ +-- lsp-set-addresses ls1-lp3 "f0:00:00:00:00:7 10.0.0.7 20.0.0.7" + +ovn-nbctl lsp-set-port-security ls1-lp3 "f0:00:00:00:00:07 10.0.0.7 20.0.0.7" + ovn-nbctl ls-add ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ -- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4" @@ -3502,13 +3507,17 @@ ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 30.0.0.7" ovn-nbctl -- --id=@d1 create DHCP_Options cidr=10.0.0.0/24 \ options="\"server_id\"=\"10.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:01\" \ \"lease_time\"=\"3600\" \"router\"=\"10.0.0.1\"" \ --- add Logical_Switch_Port ls1-lp1 dhcpv4_options @d1 \ --- add Logical_Switch_Port ls1-lp2 dhcpv4_options @d1 +-- add Logical_Switch ls1 default_dhcpv4_options @d1 ovn-nbctl -- --id=@d2 create DHCP_Options cidr=30.0.0.0/24 \ options="\"server_id\"=\"30.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:02\" \ \"lease_time\"=\"3600\"" -- add Logical_Switch_Port ls2-lp2 dhcpv4_options @d2 +ovn-nbctl -- --id=@d3 create DHCP_Options cidr=20.0.0.0/24 \ +options="\"server_id\"=\"20.0.0.1\" \"server_mac\"=\"ff:20:00:00:00:01\" \ +\"lease_time\"=\"3600\" \"router\"=\"20.0.0.1\"" \ +-- add Logical_Switch_Port ls1-lp3 dhcpv4_options @d3 + net_add n1 sim_add hv1 @@ -3539,6 +3548,12 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \ options:rxq_pcap=hv1/vif4-rx.pcap \ ofport-request=4 +ovs-vsctl -- add-port br-int hv1-vif5 -- \ + set interface hv1-vif5 external-ids:iface-id=ls1-lp3 \ + options:tx_pcap=hv1/vif5-tx.pcap \ + options:rxq_pcap=hv1/vif5-rx.pcap \ + ofport-request=5 + ovn_populate_arp sleep 2 @@ -3724,6 +3739,25 @@ OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected]) OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected]) +# Send DHCPREQUEST for ls1-lp3, it refers a specific dhcpv4_options then +# default dhcpv4_options in ls1. +offer_ip=`ip_to_hex 20 0 0 7` +server_ip=`ip_to_hex 20 0 0 1` +expected_dhcp_opts=0104ffffff00030414000001360414000001330400000e10 +reset_pcap_file hv1-vif5 hv1/vif5 +rm -f 5.expected +test_dhcp 5 f00000000007 03 $offer_ip ff2000000001 $server_ip $expected_dhcp_opts + +# NXT_RESUMEs should be 4. +OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap > 5.packets +cat 5.expected | cut -c -48 > expout +AT_CHECK([cat 5.packets | cut -c -48], [0], [expout]) +# Skipping the IPv4 checksum. +cat 5.expected | cut -c 53- > expout +AT_CHECK([cat 5.packets | cut -c 53-], [0], [expout]) + as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) @@ -3762,8 +3796,7 @@ ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 ae70::5" ovn-nbctl -- --id=@d1 create DHCP_Options cidr="ae70\:\:/64" \ options="\"server_id\"=\"00:00:00:10:00:01\"" \ --- add Logical_Switch_Port ls1-lp1 dhcpv6_options @d1 \ --- add Logical_Switch_Port ls1-lp2 dhcpv6_options @d1 +-- add Logical_Switch ls1 default_dhcpv6_options @d1 \ ovn-nbctl ls-add ls2 ovn-nbctl lsp-add ls2 ls2-lp1 \ @@ -3773,6 +3806,15 @@ ovn-nbctl lsp-add ls2 ls2-lp2 \ -- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 be70::4" ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 be70::4" +ovn-nbctl lsp-add ls1 ls1-lp3 \ +-- lsp-set-addresses ls1-lp3 "f0:00:00:00:00:07 ae70::7 cd80::7" + +ovn-nbctl lsp-set-port-security ls1-lp3 "f0:00:00:00:00:07 ae70::7 cd80::7" + +ovn-nbctl -- --id=@d3 create DHCP_Options cidr="cd80\:\:/64" \ +options="\"server_id\"=\"00:00:00:10:00:01\"" \ +-- add Logical_Switch_Port ls1-lp3 dhcpv6_options @d3 \ + net_add n1 sim_add hv1 @@ -3803,6 +3845,12 @@ ovs-vsctl -- add-port br-int hv1-vif4 -- \ options:rxq_pcap=hv1/vif4-rx.pcap \ ofport-request=4 +ovs-vsctl -- add-port br-int hv1-vif5 -- \ + set interface hv1-vif5 external-ids:iface-id=ls1-lp3 \ + options:tx_pcap=hv1/vif5-tx.pcap \ + options:rxq_pcap=hv1/vif5-rx.pcap \ + ofport-request=5 + ovn_populate_arp sleep 2 @@ -3950,6 +3998,24 @@ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif4-tx.pcap | trim_zeros > 4.pa cat 4.expected > expout AT_CHECK([cat 4.packets], [0], [expout]) +# Test for ls1-lp3, it has a specific dhcp options than default dhcp options in ls1. +src_mac=f00000000007 +src_lla=fe80000000000000f20000fffe000007 +offer_ip=cd800000000000000000000000000007 +reset_pcap_file hv1-vif5 hv1/vif5 +rm 5.packets +test_dhcpv6 5 $src_mac $src_lla 01 $offer_ip + +## NXT_RESUMEs should be 3. +#OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap | trim_zeros > 5.packets +cat 5.expected | cut -c -120 > expout +AT_CHECK([cat 5.packets | cut -c -120], [0], [expout]) +# Skipping the UDP checksum +cat 5.expected | cut -c 125- > expout +AT_CHECK([cat 5.packets | cut -c 125-], [0], [expout]) + as hv1 OVS_APP_EXIT_AND_WAIT([ovn-controller]) OVS_APP_EXIT_AND_WAIT([ovs-vswitchd]) -- 2.7.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev