summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--ovn/northd/ovn-northd.8.xml86
-rw-r--r--ovn/northd/ovn-northd.c184
-rw-r--r--ovn/ovn-nb.ovsschema20
-rw-r--r--ovn/ovn-nb.xml27
-rw-r--r--ovn/utilities/ovn-nbctl.c3
-rw-r--r--tests/ovn.at377
7 files changed, 685 insertions, 13 deletions
diff --git a/NEWS b/NEWS
index 99bb48e53..6337488e9 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@ Post-v2.7.0
- In ovn-vsctl and vtep-ctl, record UUIDs in commands may now be
abbreviated to 4 hex digits.
- OVN:
+ * New built-in DNS support.
* IPAM for IPv4 can now exclude user-defined addresses from assignment.
* IPAM can now assign IPv6 addresses.
* Make the DHCPv4 router setting optional.
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index 8021e2abb..c0b4c5eb0 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -695,7 +695,71 @@ output;
</li>
</ul>
- <h3>Ingress Table 13 Destination Lookup</h3>
+ <h3>Ingress Table 13 DNS Lookup</h3>
+
+ <p>
+ This table looks up and resolves the DNS names to the corresponding
+ configured IP address(es).
+ </p>
+
+ <ul>
+ <li>
+ <p>
+ A priority-100 logical flow for each logical switch datapath
+ if it is configured with DNS records, which matches the IPv4 and IPv6
+ packets with <code>udp.dst</code> = 53 and applies the action
+ <code>dns_lookup</code> and advances the packet to the next table.
+ </p>
+
+ <pre>
+reg0[4] = dns_lookup(); next;
+ </pre>
+
+ <p>
+ For valid DNS packets, this transforms the packet into a DNS
+ reply if the DNS name can be resolved, and stores 1 into reg0[4].
+ For failed DNS resolution or other kinds of packets, it just stores
+ 0 into reg0[4]. Either way, it continues to the next table.
+ </p>
+ </li>
+ </ul>
+
+ <h3>Ingress Table 14 DNS Responses</h3>
+
+ <p>
+ This table implements DNS responder for the DNS replies generated by
+ the previous table.
+ </p>
+
+ <ul>
+ <li>
+ <p>
+ A priority-100 logical flow for each logical switch datapath
+ if it is configured with DNS records, which matches the IPv4 and IPv6
+ packets with <code>udp.dst = 53 &amp;&amp; reg0[4] == 1</code>
+ and responds back to the <code>inport</code> after applying these
+ actions. If <code>reg0[4]</code> is set to 1, it means that the
+ action <code>dns_lookup</code> was successful.
+ </p>
+
+ <pre>
+eth.dst &lt;-&gt; eth.src;
+ip4.src &lt;-&gt; ip4.dst;
+udp.dst = udp.src;
+udp.src = 53;
+outport = <var>P</var>;
+flags.loopback = 1;
+output;
+ </pre>
+
+ <p>
+ (This terminates ingress packet processing; the packet does not go
+ to the next ingress table.)
+ </p>
+ </li>
+ </ul>
+
+ <h3>Ingress Table 15 Destination Lookup</h3>
<p>
This table implements switching behavior. It contains these logical
@@ -805,11 +869,23 @@ output;
</p>
<p>
- Also a priority 34000 logical flow is added for each logical port which
- has DHCPv4 options defined to allow the DHCPv4 reply packet and which has
- DHCPv6 options defined to allow the DHCPv6 reply packet from the
- <code>Ingress Table 12: DHCP responses</code>.
+ Also the following flows are added.
</p>
+ <ul>
+ <li>
+ A priority 34000 logical flow is added for each logical port which
+ has DHCPv4 options defined to allow the DHCPv4 reply packet and which has
+ DHCPv6 options defined to allow the DHCPv6 reply packet from the
+ <code>Ingress Table 12: DHCP responses</code>.
+ </li>
+
+ <li>
+ A priority 34000 logical flow is added for each logical switch datapath
+ configured with DNS records with the match <code>udp.dst = 53</code>
+ to allow the DNS reply packet from the
+ <code>Ingress Table 14:DNS responses</code>.
+ </li>
+ </ul>
<h3>Egress Table 7: Egress Port Security - IP</h3>
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index d0a5ba200..96aad9ae7 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -112,7 +112,9 @@ enum ovn_stage {
PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 10, "ls_in_arp_rsp") \
PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 11, "ls_in_dhcp_options") \
PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 12, "ls_in_dhcp_response") \
- PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 13, "ls_in_l2_lkup") \
+ PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 13, "ls_in_dns_lookup") \
+ PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 14, "ls_in_dns_response") \
+ PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 15, "ls_in_l2_lkup") \
\
/* Logical switch egress stages. */ \
PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
@@ -160,6 +162,7 @@ enum ovn_stage {
#define REGBIT_CONNTRACK_COMMIT "reg0[1]"
#define REGBIT_CONNTRACK_NAT "reg0[2]"
#define REGBIT_DHCP_OPTS_RESULT "reg0[3]"
+#define REGBIT_DNS_LOOKUP_RESULT "reg0[4]"
/* Register definitions for switches and routers. */
#define REGBIT_NAT_REDIRECT "reg9[0]"
@@ -2669,6 +2672,22 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address,
free(start);
}
+/*
+ * Returns true if logical switch is configured with DNS records, false
+ * otherwise.
+ */
+static bool
+ls_has_dns_records(const struct nbrec_logical_switch *nbs)
+{
+ for (size_t i = 0; i < nbs->n_dns_records; i++) {
+ if (!smap_is_empty(&nbs->dns_records[i]->records)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void
build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
{
@@ -2961,7 +2980,8 @@ 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*/
+ * logical ports of the datapath if the CMS has configured DHCPv4 options.
+ * */
for (size_t i = 0; i < od->nbs->n_ports; i++) {
if (od->nbs->ports[i]->dhcpv4_options) {
const char *server_id = smap_get(
@@ -3012,6 +3032,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
}
}
}
+
+ /* Add a 34000 priority flow to advance the DNS reply from ovn-controller,
+ * if the CMS has configured DNS records for the datapath.
+ */
+ if (ls_has_dns_records(od->nbs)) {
+ const char *actions = has_stateful ? "ct_commit; next;" : "next;";
+ ovn_lflow_add(
+ lflows, od, S_SWITCH_OUT_ACL, 34000, "udp.src == 53",
+ actions);
+ }
}
static void
@@ -3449,8 +3479,44 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
}
}
+ /* Logical switch ingress table 13 and 14: DNS lookup and response
+ * priority 100 flows.
+ */
+ HMAP_FOR_EACH (od, key_node, datapaths) {
+ if (!od->nbs || !ls_has_dns_records(od->nbs)) {
+ continue;
+ }
+
+ struct ds match;
+ struct ds action;
+ ds_init(&match);
+ ds_init(&action);
+ ds_put_cstr(&match, "udp.dst == 53");
+ ds_put_format(&action,
+ REGBIT_DNS_LOOKUP_RESULT" = dns_lookup(); next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 100,
+ ds_cstr(&match), ds_cstr(&action));
+ ds_clear(&action);
+ ds_put_cstr(&match, " && "REGBIT_DNS_LOOKUP_RESULT);
+ ds_put_format(&action, "eth.dst <-> eth.src; ip4.src <-> ip4.dst; "
+ "udp.dst = udp.src; udp.src = 53; outport = inport; "
+ "flags.loopback = 1; output;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 100,
+ ds_cstr(&match), ds_cstr(&action));
+ ds_clear(&action);
+ ds_put_format(&action, "eth.dst <-> eth.src; ip6.src <-> ip6.dst; "
+ "udp.dst = udp.src; udp.src = 53; outport = inport; "
+ "flags.loopback = 1; output;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 100,
+ ds_cstr(&match), ds_cstr(&action));
+ ds_destroy(&match);
+ ds_destroy(&action);
+ }
+
/* Ingress table 11 and 12: DHCP options and response, by default goto next.
- * (priority 0). */
+ * (priority 0).
+ * Ingress table 13 and 14: DNS lookup and response, by default goto next.
+ * (priority 0).*/
HMAP_FOR_EACH (od, key_node, datapaths) {
if (!od->nbs) {
@@ -3459,9 +3525,11 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 0, "1", "next;");
ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
+ ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1", "next;");
}
- /* Ingress table 13: Destination lookup, broadcast and multicast handling
+ /* Ingress table 15: Destination lookup, broadcast and multicast handling
* (priority 100). */
HMAP_FOR_EACH (op, key_node, ports) {
if (!op->nbsp) {
@@ -5408,6 +5476,108 @@ sync_address_sets(struct northd_context *ctx)
}
shash_destroy(&sb_address_sets);
}
+
+/*
+ * struct 'dns_info' is used to sync the DNS records between OVN Northbound db
+ * and Southbound db.
+ */
+struct dns_info {
+ struct hmap_node hmap_node;
+ const struct nbrec_dns *nb_dns; /* DNS record in the Northbound db. */
+ const struct sbrec_dns *sb_dns; /* DNS record in the Soutbound db. */
+
+ /* Datapaths to which the DNS entry is associated with it. */
+ const struct sbrec_datapath_binding **sbs;
+ size_t n_sbs;
+};
+
+static inline struct dns_info *
+get_dns_info_from_hmap(struct hmap *dns_map, struct uuid *uuid)
+{
+ struct dns_info *dns_info;
+ size_t hash = uuid_hash(uuid);
+ HMAP_FOR_EACH_WITH_HASH (dns_info, hmap_node, hash, dns_map) {
+ if (uuid_equals(&dns_info->nb_dns->header_.uuid, uuid)) {
+ return dns_info;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
+{
+ struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
+ struct ovn_datapath *od;
+ HMAP_FOR_EACH (od, key_node, datapaths) {
+ if (!od->nbs || !od->nbs->n_dns_records) {
+ continue;
+ }
+
+ for (size_t i = 0; i < od->nbs->n_dns_records; i++) {
+ struct dns_info *dns_info = get_dns_info_from_hmap(
+ &dns_map, &od->nbs->dns_records[i]->header_.uuid);
+ if (!dns_info) {
+ size_t hash = uuid_hash(
+ &od->nbs->dns_records[i]->header_.uuid);
+ dns_info = xzalloc(sizeof *dns_info);;
+ dns_info->nb_dns = od->nbs->dns_records[i];
+ hmap_insert(&dns_map, &dns_info->hmap_node, hash);
+ }
+
+ dns_info->n_sbs++;
+ dns_info->sbs = xrealloc(dns_info->sbs,
+ dns_info->n_sbs * sizeof *dns_info->sbs);
+ dns_info->sbs[dns_info->n_sbs - 1] = od->sb;
+ }
+ }
+
+ const struct sbrec_dns *sbrec_dns, *next;
+ SBREC_DNS_FOR_EACH_SAFE (sbrec_dns, next, ctx->ovnsb_idl) {
+ const char *nb_dns_uuid = smap_get(&sbrec_dns->external_ids, "dns_id");
+ struct uuid dns_uuid;
+ if (!nb_dns_uuid || !uuid_from_string(&dns_uuid, nb_dns_uuid)) {
+ sbrec_dns_delete(sbrec_dns);
+ continue;
+ }
+
+ struct dns_info *dns_info =
+ get_dns_info_from_hmap(&dns_map, &dns_uuid);
+ if (dns_info) {
+ dns_info->sb_dns = sbrec_dns;
+ } else {
+ sbrec_dns_delete(sbrec_dns);
+ }
+ }
+
+ struct dns_info *dns_info;
+ HMAP_FOR_EACH_POP (dns_info, hmap_node, &dns_map) {
+ if (!dns_info->sb_dns) {
+ struct sbrec_dns *sbrec_dns = sbrec_dns_insert(ctx->ovnsb_txn);
+ dns_info->sb_dns = sbrec_dns;
+ char *dns_id = xasprintf(
+ UUID_FMT, UUID_ARGS(&dns_info->nb_dns->header_.uuid));
+ const struct smap external_ids =
+ SMAP_CONST1(&external_ids, "dns_id", dns_id);
+ sbrec_dns_set_external_ids(sbrec_dns, &external_ids);
+ free(dns_id);
+ }
+
+ /* Set the datapaths and records. If nothing has changed, then
+ * this will be a no-op.
+ */
+ sbrec_dns_set_datapaths(
+ dns_info->sb_dns,
+ (struct sbrec_datapath_binding **)dns_info->sbs,
+ dns_info->n_sbs);
+ sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
+ free(dns_info->sbs);
+ free(dns_info);
+ }
+ hmap_destroy(&dns_map);
+}
+
static void
ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)
@@ -5422,6 +5592,7 @@ ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)
build_lflows(ctx, &datapaths, &ports);
sync_address_sets(ctx);
+ sync_dns_entries(ctx, &datapaths);
struct ovn_datapath *dp, *next_dp;
HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, &datapaths) {
@@ -5821,6 +5992,11 @@ main(int argc, char *argv[])
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dns);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_datapaths);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_records);
+ add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_external_ids);
+
ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 5033d170a..86efe8642 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
- "version": "5.5.1",
- "cksum": "1924970369 14236",
+ "version": "5.6.0",
+ "cksum": "1358108512 15019",
"tables": {
"NB_Global": {
"columns": {
@@ -45,6 +45,11 @@
"refType": "strong"},
"min": 0,
"max": "unlimited"}},
+ "dns_records": {"type": {"key": {"type": "uuid",
+ "refTable": "DNS",
+ "refType": "weak"},
+ "min": 0,
+ "max": "unlimited"}},
"other_config": {
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}},
@@ -265,6 +270,17 @@
"max": "unlimited"},
"ephemeral": true}},
"indexes": [["target"]]},
+ "DNS": {
+ "columns": {
+ "records": {"type": {"key": "string",
+ "value": "string",
+ "min": 0,
+ "max": "unlimited"}},
+ "external_ids": {"type": {"key": "string",
+ "value": "string",
+ "min": 0,
+ "max": "unlimited"}}},
+ "isRoot": true},
"SSL": {
"columns": {
"private_key": {"type": "string"},
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 7a1c20e73..deff96513 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -134,6 +134,12 @@
QOS marking rules that apply to packets within the logical switch.
</column>
+ <column name="dns_records">
+ This column defines the DNS records to be used for resolving internal
+ DNS queries within the logical switch by the native DNS resolver.
+ Please see the <ref table="DNS"/> table.
+ </column>
+
<group title="IP Address Assignment">
<p>
These options control automatic IP address management (IPAM) for ports
@@ -1474,7 +1480,7 @@
<column name="options" key="lease_time"
type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967295}'>
<p>
- The offered lease time in seconds,
+ The offered lease time in seconds,
</p>
<p>
@@ -1966,6 +1972,24 @@
<column name="other_config"/>
</group>
</table>
+ <table name="DNS" title="Native DNS resolution">
+ <p>
+ Each row in this table stores the DNS records. The
+ <ref table="Logical_Switch"/> table's <ref table="Logical_Switch"
+ column="dns_records"/> references these records.
+ </p>
+
+ <column name="records">
+ Key-value pair of DNS records with <code>DNS query name</code> as the key
+ and value as a string of IP address(es) separated by comma or space.
+
+ <p><b>Example: </b> "vm1.ovn.org" = "10.0.0.4 aef0::4"</p>
+ </column>
+
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </table>
<table name="SSL">
SSL configuration for ovn-nb database access.
@@ -2004,5 +2028,4 @@
<column name="external_ids"/>
</group>
</table>
-
</database>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index e5999a654..b18dd65d9 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -3063,6 +3063,9 @@ static const struct ctl_table_class tables[NBREC_N_TABLES] = {
[NBREC_TABLE_SSL].row_ids[0]
= {&nbrec_table_nb_global, NULL, &nbrec_nb_global_col_ssl},
+
+ [NBREC_TABLE_DNS].row_ids[0]
+ = {&nbrec_table_dns, NULL, &nbrec_dns_col_records},
};
static void
diff --git a/tests/ovn.at b/tests/ovn.at
index bace273b7..e67d33b77 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -6435,6 +6435,383 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
AT_CLEANUP
+AT_SETUP([ovn -- dns lookup : 1 HV, 2 LS, 2 LSPs/LS])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+ovn-nbctl ls-add ls1
+
+ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4"
+
+ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4"
+
+ovn-nbctl lsp-add ls1 ls1-lp2 \
+-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
+
+ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
+
+DNS1=`ovn-nbctl create DNS records={}`
+DNS2=`ovn-nbctl create DNS records={}`
+
+ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
+ovn-nbctl set DNS $DNS1 records:vm2.ovn.org="10.0.0.6 20.0.0.4"
+ovn-nbctl set DNS $DNS2 records:vm3.ovn.org="40.0.0.4"
+
+ovn-nbctl set Logical_switch ls1 dns_records="$DNS1"
+
+net_add n1
+sim_add hv1
+
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+ set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+ options:tx_pcap=hv1/vif1-tx.pcap \
+ options:rxq_pcap=hv1/vif1-rx.pcap \
+ ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+ set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
+ options:tx_pcap=hv1/vif2-tx.pcap \
+ options:rxq_pcap=hv1/vif2-rx.pcap \
+ ofport-request=2
+
+ovn_populate_arp
+sleep 2
+as hv1 ovs-vsctl show
+
+echo "*************************"
+ovn-sbctl list DNS
+echo "*************************"
+
+ip_to_hex() {
+ printf "%02x%02x%02x%02x" "$@"
+}
+
+reset_pcap_file() {
+ local iface=$1
+ local pcap_file=$2
+ ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+ rm -f ${pcap_file}*.pcap
+ ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+# set_dns_params host_name
+# Sets the dns_req_data and dns_resp_data
+set_dns_params() {
+ local hname=$1
+ local ttl=00000e10
+ an_count=0001
+ type=0001
+ case $hname in
+ vm1)
+ # vm1.ovn.org
+ query_name=03766d31036f766e036f726700
+ # IPv4 address - 10.0.0.4
+ expected_dns_answer=${query_name}00010001${ttl}00040a000004
+ ;;
+ vm2)
+ # vm2.ovn.org
+ query_name=03766d32036f766e036f726700
+ # IPv4 address - 10.0.0.6
+ expected_dns_answer=${query_name}00010001${ttl}00040a000006
+ # IPv4 address - 20.0.0.4
+ expected_dns_answer=${expected_dns_answer}${query_name}00010001${ttl}000414000004
+ an_count=0002
+ ;;
+ vm3)
+ # vm3.ovn.org
+ query_name=03766d33036f766e036f726700
+ # IPv4 address - 40.0.0.4
+ expected_dns_answer=${query_name}00010001${ttl}000428000004
+ ;;
+ vm1_ipv6_only)
+ # vm1.ovn.org
+ query_name=03766d31036f766e036f726700
+ # IPv6 address - aef0::4
+ type=001c
+ expected_dns_answer=${query_name}${type}0001${ttl}0010aef00000000000000000000000000004
+ ;;
+ vm1_ipv4_v6)
+ # vm1.ovn.org
+ query_name=03766d31036f766e036f726700
+ type=00ff
+ an_count=0002
+ # IPv4 address - 10.0.0.4
+ # IPv6 address - aef0::4
+ expected_dns_answer=${query_name}00010001${ttl}00040a000004
+ expected_dns_answer=${expected_dns_answer}${query_name}001c0001${ttl}0010
+ expected_dns_answer=${expected_dns_answer}aef00000000000000000000000000004
+ ;;
+ vm1_invalid_type)
+ # vm1.ovn.org
+ query_name=03766d31036f766e036f726700
+ # IPv6 address - aef0::4
+ type=0002
+ ;;
+ vm1_incomplete)
+ # set type to none
+ type=''
+ esac
+ # TTL - 3600
+ local dns_req_header=010201200001000000000000
+ local dns_resp_header=010281200001${an_count}00000000
+ dns_req_data=${dns_req_header}${query_name}${type}0001
+ dns_resp_data=${dns_resp_header}${query_name}${type}0001${expected_dns_answer}
+}
+
+# This shell function sends a DNS request packet
+# test_dns INPORT SRC_MAC DST_MAC SRC_IP DST_IP DNS_QUERY EXPEC
+test_dns() {
+ local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 dns_reply=$6
+ local dns_query_data=$7
+ shift; shift; shift; shift; shift; shift; shift;
+ # Packet size => IPv4 header (20) + UDP header (8) +
+ # DNS data (header + query)
+ ip_len=`expr 28 + ${#dns_query_data} / 2`
+ udp_len=`expr $ip_len - 20`
+ ip_len=$(printf "%x" $ip_len)
+ udp_len=$(printf "%x" $udp_len)
+ local request=${dst_mac}${src_mac}0800450000${ip_len}0000000080110000
+ request=${request}${src_ip}${dst_ip}9234003500${udp_len}0000
+ # dns data
+ request=${request}${dns_query_data}
+
+ if test $dns_reply != 0; then
+ local dns_reply=$1
+ ip_len=`expr 28 + ${#dns_reply} / 2`
+ udp_len=`expr $ip_len - 20`
+ ip_len=$(printf "%x" $ip_len)
+ udp_len=$(printf "%x" $udp_len)
+ local reply=${src_mac}${dst_mac}0800450000${ip_len}0000000080110000
+ reply=${reply}${dst_ip}${src_ip}0035923400${udp_len}0000${dns_reply}
+ echo $reply >> $inport.expected
+ else
+ for outport; do
+ echo $request >> $outport.expected
+ done
+ fi
+ as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
+}
+
+AT_CAPTURE_FILE([ofctl_monitor0.log])
+as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
+--pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
+
+set_dns_params vm2
+src_ip=`ip_to_hex 10 0 0 4`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=1
+test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+
+# NXT_RESUMEs should be 1.
+OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+cat 1.expected | cut -c -48 > expout
+AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 1.expected | cut -c 53- > expout
+AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+set_dns_params vm1
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=1
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+
+# NXT_RESUMEs should be 2.
+OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Clear the query name options for ls1-lp2
+ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
+
+set_dns_params vm2
+src_ip=`ip_to_hex 10 0 0 4`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=0
+test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
+
+# NXT_RESUMEs should be 3.
+OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+AT_CHECK([cat 1.packets], [0], [])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Clear the query name for ls1-lp1
+# Since ls1 has no query names configued,
+# ovn-northd should not add the DNS flows.
+ovn-nbctl --wait=hv remove DNS $DNS1 records vm1.ovn.org
+
+set_dns_params vm1
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=0
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+
+# NXT_RESUMEs should be 3 only.
+OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+AT_CHECK([cat 2.packets], [0], [])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Test IPv6 (AAAA records) using IPv4 packet.
+# Add back the DNS options for ls1-lp1.
+ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
+
+set_dns_params vm1_ipv6_only
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=1
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+
+# 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/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Test both IPv4 (A) and IPv6 (AAAA records) using IPv4 packet.
+set_dns_params vm1_ipv4_v6
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=1
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+
+# NXT_RESUMEs should be 5.
+OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+cat 2.expected | cut -c -48 > expout
+AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 2.expected | cut -c 53- > expout
+AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Invalid type.
+set_dns_params vm1_invalid_type
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=0
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+
+# NXT_RESUMEs should be 6.
+OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+AT_CHECK([cat 2.packets], [0], [])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Incomplete DNS packet.
+set_dns_params vm1_incomplete
+src_ip=`ip_to_hex 10 0 0 6`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=0
+test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+
+# NXT_RESUMEs should be 7.
+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+AT_CHECK([cat 2.packets], [0], [])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+# Add one more DNS record to the ls1.
+ovn-nbctl --wait=hv set Logical_switch ls1 dns_records="$DNS1 $DNS2"
+
+set_dns_params vm3
+src_ip=`ip_to_hex 10 0 0 4`
+dst_ip=`ip_to_hex 10 0 0 1`
+dns_reply=1
+test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+
+# NXT_RESUMEs should be 8.
+OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+cat 1.expected | cut -c -48 > expout
+AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
+# Skipping the IPv4 checksum.
+cat 1.expected | cut -c 53- > expout
+AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+as hv1
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as main
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+AT_CLEANUP
+
AT_SETUP([ovn -- 1 LR with distributed router gateway port])
AT_SKIP_IF([test $HAVE_PYTHON = no])
ovn_start