[ovs-dev] [PATCH 2/2] ovn-northd: Add logical flows to support native DNS

nusiddiq at redhat.com nusiddiq at redhat.com
Fri Feb 10 14:32:33 UTC 2017


From: Numan Siddique <nusiddiq at redhat.com>

OVN implements native DNS resolution which can be used to resolve the
internal DNS names belonging to a logical datapath.

To support this, the logical ports should be configured with the
hostname in the 'Logical_Switch_Port.options:hostname' and an optional
fqdn in 'Logical_Switch_Port.options:fqdn'.

For each logical datapath with atleast one of its logical port
configured with hostname in the options field, following flows are
added
 - A logical flow in DNS_LKUP stage which uses the action 'dns_lkup'
   which transforms the DNS query to DNS reply packet and advances
   to the next stage - DNS_RESPONSE.

 - A logical flow in DNS_RESPONSE stage which implements the DNS responder
   by sending the DNS reply from previous stage back to the inport.

Signed-off-by: Numan Siddique <nusiddiq at redhat.com>
---
 ovn/northd/ovn-northd.8.xml |  88 ++++++++++-
 ovn/northd/ovn-northd.c     | 212 ++++++++++++++++++++++++-
 ovn/ovn-nb.xml              |  18 ++-
 tests/ovn.at                | 370 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 679 insertions(+), 9 deletions(-)

diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index ab8fd88..a3fe2a8 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -724,7 +724,73 @@ 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 of the logical ports
+      if configured with the host names.
+    </p>
+
+    <ul>
+      <li>
+        <p>
+          A priority-100 logical flow for each logical switch data path
+          if at least one of its logical port is configured with
+          <code>hostname</code>  which matches the IPv4 and IPv6 packets with
+          <code>udp.src</code> = 53 and applies the action
+          <code>dns_lkup</code> and advances the packet to the next table.
+        </p>
+
+        <pre>
+reg0[4] = dns_lkup(); 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 data path
+          if at least one of its logical port is configured with
+          <code>hostname</code> which matches IPv4 and IPv6 packets with
+          <code>udp.src == 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_lkup</code> was successful.
+        </p>
+
+        <pre>
+eth.dst &lt;&#45;&gt; eth.src;
+ip4.src &lt;&#45;&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
@@ -834,11 +900,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 for each logical switch data path if at
+        least one of its logical port is configured with hostname
+        which allows 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 a4f76a9..75a5ef0 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_LKUP,      13, "ls_in_dns_lkup") \
+    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_LKUP_RESULT  "reg0[4]"
 
 /* Register definitions for switches and routers. */
 #define REGBIT_NAT_REDIRECT     "reg9[0]"
@@ -2692,7 +2695,13 @@ 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.
+     *
+     * Add one 34000 priority flow to allow DNS reply from ovn-controller to all
+     * logical ports of the datapath if the CMS has configured DNS parameters
+     * for atleast one logical port.
+     * */
+    bool dns_flow_added = false;
     for (size_t i = 0; i < od->nbs->n_ports; i++) {
         if (od->nbs->ports[i]->dhcpv4_options) {
             const char *server_id = smap_get(
@@ -2744,6 +2753,16 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
                 ds_destroy(&match);
             }
         }
+
+        if (!dns_flow_added && smap_get(&od->nbs->ports[i]->options,
+                                        "hostname")) {
+            const char *actions = has_stateful ? "ct_commit; next;" :
+                "next;";
+            ovn_lflow_add(
+                lflows, od, S_SWITCH_OUT_ACL, 34000, "udp && udp.src == 53",
+                actions);
+            dns_flow_added = true;
+        }
     }
 }
 
@@ -3182,8 +3201,51 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
         }
     }
 
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbs) {
+           continue;
+        }
+
+        for (size_t i = 0; i < od->nbs->n_ports; i++) {
+            const struct nbrec_logical_switch_port *nbsp =
+                od->nbs->ports[i];
+
+            if (!nbsp || !od->sb || !smap_get(&nbsp->options, "hostname")) {
+                continue;
+            }
+
+            struct ds match;
+            struct ds action;
+            ds_init(&match);
+            ds_init(&action);
+            ds_put_cstr(&match, "ip && udp.dst == 53");
+            ds_put_format(&action,
+                          REGBIT_DNS_LKUP_RESULT" = dns_lkup(); next;");
+            ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LKUP, 100,
+                          ds_cstr(&match), ds_cstr(&action));
+            ds_clear(&action);
+            ds_put_cstr(&match, " && "REGBIT_DNS_LKUP_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);
+            break;
+        }
+    }
+
     /* 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) {
@@ -3192,6 +3254,8 @@ 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_LKUP, 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
@@ -5143,6 +5207,142 @@ sync_address_sets(struct northd_context *ctx)
     }
     shash_destroy(&sb_address_sets);
 }
+
+static void
+sync_dns_entries(struct northd_context *ctx, struct hmap *ports)
+{
+    struct dns_info {
+        struct hmap_node hmap_node;
+        const struct sbrec_datapath_binding *sb;
+        const char *hostname;
+        const char *fqdn;
+        char **ipv4_addresses;
+        size_t n_ipv4_addresses;
+        char **ipv6_addresses;
+        size_t n_ipv6_addresses;
+    };
+
+    struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
+    struct ovn_port *op;
+
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbsp || !op->sb || !lsp_is_enabled(op->nbsp) || op->nbrp) {
+            continue;
+        }
+
+        const char *hostname = smap_get(&op->nbsp->options, "hostname");
+        const char *fqdn = smap_get_def(&op->nbsp->options, "fqdn", "");
+
+        if (!hostname) {
+            continue;
+        }
+
+        struct dns_info *dns_info = xzalloc(sizeof *dns_info);
+        dns_info->sb = op->sb->datapath;
+        dns_info->hostname = hostname;
+        dns_info->fqdn = fqdn;
+        for (unsigned int i = 0; i < op->n_lsp_addrs; i++) {
+             for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
+                 dns_info->ipv4_addresses = xrealloc(
+                     dns_info->ipv4_addresses,
+                     sizeof *dns_info->ipv4_addresses * (
+                         dns_info->n_ipv4_addresses + 1));
+                 dns_info->ipv4_addresses[dns_info->n_ipv4_addresses++] =
+                     op->lsp_addrs[i].ipv4_addrs[j].addr_s;
+             }
+
+             for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
+                  dns_info->ipv6_addresses = xrealloc(
+                      dns_info->ipv6_addresses,
+                      sizeof *dns_info->ipv6_addresses * (
+                          dns_info->n_ipv6_addresses + 1));
+                  dns_info->ipv6_addresses[dns_info->n_ipv6_addresses++] =
+                      op->lsp_addrs[i].ipv6_addrs[j].addr_s;
+             }
+        }
+        size_t hash = uuid_hash(&dns_info->sb->header_.uuid);
+        hash = hash_string(dns_info->hostname, hash);
+        hash = hash_string(dns_info->fqdn, hash);
+        hmap_insert(&dns_map, &dns_info->hmap_node, hash);
+    }
+
+    const struct sbrec_dns *sbrec_dns, *next;
+    SBREC_DNS_FOR_EACH_SAFE(sbrec_dns, next, ctx->ovnsb_idl) {
+        size_t hash = uuid_hash(&sbrec_dns->datapath->header_.uuid);
+        hash = hash_string(sbrec_dns->hostname, hash);
+        hash = hash_string(sbrec_dns->fqdn, hash);
+        bool delete_dns_record = true;
+        struct dns_info *dns_info;
+        HMAP_FOR_EACH_WITH_HASH(dns_info, hmap_node, hash, &dns_map) {
+            if (!strcmp(dns_info->hostname, sbrec_dns->hostname)) {
+                /* Verify that the ipv4_addresses and ipv6_addresses are
+                 * same before removing from the hmap. */
+                if ((dns_info->n_ipv4_addresses !=
+                     sbrec_dns->n_ipv4_addresses) ||
+                    (dns_info->n_ipv6_addresses !=
+                     sbrec_dns->n_ipv6_addresses)) {
+                    continue;
+                }
+
+                delete_dns_record = false;
+                for (size_t i = 0; i < sbrec_dns->n_ipv4_addresses; i++) {
+                    if (strcmp(dns_info->ipv4_addresses[i],
+                               sbrec_dns->ipv4_addresses[i])) {
+                        delete_dns_record = true;
+                        break;
+                    }
+                }
+
+                if (delete_dns_record) {
+                    continue;
+                }
+
+                delete_dns_record = false;
+                for (size_t i = 0; i < sbrec_dns->n_ipv6_addresses; i++) {
+                    if (strcmp(dns_info->ipv6_addresses[i],
+                               sbrec_dns->ipv6_addresses[i])) {
+                        delete_dns_record = true;
+                        break;
+                    }
+                }
+
+                if (delete_dns_record) {
+                    continue;
+                }
+
+                hmap_remove(&dns_map, &dns_info->hmap_node);
+                free(dns_info->ipv4_addresses);
+                free(dns_info->ipv6_addresses);
+                free(dns_info);
+                delete_dns_record = false;
+                break;
+            }
+        }
+
+        if (delete_dns_record) {
+            sbrec_dns_delete(sbrec_dns);
+        }
+    }
+
+    struct dns_info *dns_info;
+    HMAP_FOR_EACH_POP(dns_info, hmap_node, &dns_map) {
+        struct sbrec_dns *sbrec_dns = sbrec_dns_insert(ctx->ovnsb_txn);
+        sbrec_dns_set_datapath(sbrec_dns, dns_info->sb);
+        sbrec_dns_set_hostname(sbrec_dns, dns_info->hostname);
+        sbrec_dns_set_fqdn(sbrec_dns, dns_info->fqdn);
+        sbrec_dns_set_ipv4_addresses(
+            sbrec_dns, (const char **) dns_info->ipv4_addresses,
+            dns_info->n_ipv4_addresses);
+        sbrec_dns_set_ipv6_addresses(
+            sbrec_dns, (const char **) dns_info->ipv6_addresses,
+            dns_info->n_ipv6_addresses);
+        free(dns_info->ipv4_addresses);
+        free(dns_info->ipv6_addresses);
+        free(dns_info);
+    }
+    hmap_destroy(&dns_map);
+}
+
 
 static void
 ovnnb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop)
@@ -5157,6 +5357,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, &ports);
 
     struct ovn_datapath *dp, *next_dp;
     HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, &datapaths) {
@@ -5554,6 +5755,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_datapath);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_hostname);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_fqdn);
+
     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.xml b/ovn/ovn-nb.xml
index c5ebbea..703b36f 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -316,6 +316,23 @@
           If set, indicates the maximum burst size for data sent from this
           interface, in bits.
         </column>
+
+        <column name="options" key="hostname">
+          If set, indicates the hostname associated with the logical port.
+          <code>OVN</code> supports a simple native internal DNS
+          resolution. Any DNS query for this host name from the VIFs belonging
+          to the same logical network would be resolved by
+          <code>OVN</code> to the IP addresses defined in the
+          <ref column="addresses"/> field. If no match is found, the DNS query
+          request is expected to be handled by an external DNS resolver.
+        </column>
+
+        <column name="options" key="fqdn">
+          If set, indicates the fqdn associated with the logical port.
+          Any DNS query for this fqdn would be resolved by
+          <code>ovn-controller</code> to the IP addresses defined in the
+          <ref column="addresses"/> field.
+        </column>
       </group>
     </group>
 
@@ -1898,5 +1915,4 @@
       <column name="external_ids"/>
     </group>
   </table>
-
 </database>
diff --git a/tests/ovn.at b/tests/ovn.at
index cebe1f1..f08bace 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -6254,6 +6254,376 @@ 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"
+
+ovn-nbctl lsp-set-options ls1-lp1 hostname=vm1 fqdn=vm1.ovn.org
+ovn-nbctl lsp-set-options ls1-lp2 hostname=vm2 fqdn=vm2.ovn.org
+
+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_lsp_dns_params lsp_name
+# Sets the dns_req_data and dns_resp_data
+set_lsp_dns_params() {
+    local lsp_name=$1
+    local ttl=00000e10
+    an_count=0001
+    type=0001
+    case $lsp_name in
+    ls1-lp1)
+        # vm1
+        hostname=03766d3100
+        # IPv4 address - 10.0.0.4
+        expected_dns_answer=${hostname}00010001${ttl}00040a000004
+        ;;
+    ls1-lp1_fqdn)
+        # vm1.ovn.org
+        hostname=03766d31036f766e036f726700
+        # IPv4 address - 10.0.0.4
+        expected_dns_answer=${hostname}00010001${ttl}00040a000004
+        ;;
+    ls1-lp2)
+        # vm2
+        hostname=03766d3200
+        # IPv4 address - 10.0.0.6
+        expected_dns_answer=${hostname}00010001${ttl}00040a000006
+        # IPv4 address - 20.0.0.4
+        expected_dns_answer=${expected_dns_answer}${hostname}00010001${ttl}000414000004
+        an_count=0002
+        ;;
+    ls1-lp2_fqdn)
+        # vm2.ovn.org.
+        # ls1-lp2 is not configured with fqdn.
+        hostname=03766d32036f766e036f726700
+        ;;
+    ls1-lp1_ipv6_only)
+        # vm1.ovn.org
+        hostname=03766d31036f766e036f726700
+        # IPv6 address - aef0::4
+        type=001c
+        expected_dns_answer=${hostname}${type}0001${ttl}0010aef00000000000000000000000000004
+        ;;
+    ls1-lp1_ipv4_v6)
+        # vm1.ovn.org
+        hostname=03766d31036f766e036f726700
+        type=00ff
+        an_count=0002
+        # IPv4 address - 10.0.0.4
+        # IPv6 address - aef0::4
+        expected_dns_answer=${hostname}00010001${ttl}00040a000004
+        expected_dns_answer=${expected_dns_answer}${hostname}001c0001${ttl}0010
+        expected_dns_answer=${expected_dns_answer}aef00000000000000000000000000004
+        ;;
+    ls1-lp1_invalid_type)
+        # vm1.ovn.org
+        hostname=03766d31036f766e036f726700
+        # IPv6 address - aef0::4
+        type=0002
+        ;;
+    ls1-lp1_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}${hostname}${type}0001
+    dns_resp_data=${dns_resp_header}${hostname}${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_lsp_dns_params ls1-lp2
+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_lsp_dns_params ls1-lp1
+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 hostname options for ls1-lp2
+ovn-nbctl lsp-set-options ls1-lp2
+
+set_lsp_dns_params ls1-lp2
+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 hostname for ls1-lp1 (but keep fqdn).
+# Since no ports of ls1 has hostname configued,
+# ovn-northd should not add the DNS flows.
+ovn-nbctl lsp-set-options ls1-lp1 fqdn=vm1.ovn.org
+set_lsp_dns_params ls1-lp1
+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 lsp-set-options ls1-lp1 hostname=vm1 fqdn=vm1.ovn.org
+
+set_lsp_dns_params ls1-lp1_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_lsp_dns_params ls1-lp1_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_lsp_dns_params ls1-lp1_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_lsp_dns_params ls1-lp1_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
+
+# Don't configure fqdn for ls1-lp2 and query for fqdn.
+ovn-nbctl lsp-set-options ls1-lp2 hostname=vm2
+set_lsp_dns_params ls1-lp2_fqdn
+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 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_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/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
+
+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
-- 
2.9.3



More information about the dev mailing list