@@ -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.
@@ -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"}}},
@@ -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>
@@ -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 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 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])