Add support for FQDN option (39), if specified the server
can overwrite FQDN for the client. It behaves similarly
to DHCP hostname option (12).

Reported-at: https://bugzilla.redhat.com/2211890
Signed-off-by: Ales Musil <amu...@redhat.com>
---
v2: Rebase on top of current main.
    Address comments from Mark:
    - Properly propagate option flags.
    - Extract the fqdn encoding to common function.
---
 NEWS                 |   2 +
 controller/pinctrl.c |  69 +++++++++----------
 lib/actions.c        |   9 ++-
 lib/ovn-l7.h         |  10 +++
 lib/ovn-util.c       |  27 ++++++++
 lib/ovn-util.h       |   5 ++
 northd/ovn-northd.c  |   3 +-
 ovn-nb.xml           |   7 ++
 ovn-sb.ovsschema     |   6 +-
 tests/ovn.at         | 155 ++++++++++++++++++++++++-------------------
 tests/test-ovn.c     |   1 +
 11 files changed, 181 insertions(+), 113 deletions(-)

diff --git a/NEWS b/NEWS
index 645acea1f..e6c87e9ad 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 Post v23.06.0
 -------------
+  - Add DHCPv6 "fqdn" (39) option, that works similarly to
+    DHCPv4 "hostname" (12) option.
 
 OVN v23.06.0 - 01 Jun 2023
 --------------------------
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index c396ad4c2..d7bd20ed6 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -2288,7 +2288,7 @@ exit:
 static bool
 compose_out_dhcpv6_opts(struct ofpbuf *userdata,
                         struct ofpbuf *out_dhcpv6_opts,
-                        ovs_be32 iaid, bool ipxe_req)
+                        ovs_be32 iaid, bool ipxe_req, uint8_t fqdn_flags)
 {
     while (userdata->size) {
         struct dhcpv6_opt_header *userdata_opt = ofpbuf_try_pull(
@@ -2412,6 +2412,24 @@ compose_out_dhcpv6_opts(struct ofpbuf *userdata,
             break;
         }
 
+        case DHCPV6_OPT_FQDN_CODE: {
+            if (fqdn_flags != DHCPV6_FQDN_FLAGS_UNDEFINED) {
+                struct dhcpv6_opt_header *header =
+                        ofpbuf_put_zeros(out_dhcpv6_opts, sizeof *header);
+                header->code = htons(DHCPV6_OPT_FQDN_CODE);
+                header->len = htons(size + 1);
+                uint8_t *flags = ofpbuf_put_zeros(out_dhcpv6_opts, 1);
+                /* Always set N to 1, if client requested S inform him that it
+                 * was overwritten by the server. */
+                *flags |= DHCPV6_FQDN_FLAGS_N;
+                if (fqdn_flags & DHCPV6_FQDN_FLAGS_S) {
+                    *flags |= DHCPV6_FQDN_FLAGS_O;
+                }
+                ofpbuf_put(out_dhcpv6_opts, userdata_opt_data, size);
+            }
+            break;
+        }
+
         default:
             return false;
         }
@@ -2500,6 +2518,7 @@ pinctrl_handle_put_dhcpv6_opts(
     size_t l4_len = dp_packet_l4_size(pkt_in);
     uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
     bool ipxe_req = false;
+    uint8_t fqdn_flags = DHCPV6_FQDN_FLAGS_UNDEFINED;
     while (in_dhcpv6_data < end) {
         struct dhcpv6_opt_header const *in_opt =
              (struct dhcpv6_opt_header *)in_dhcpv6_data;
@@ -2524,6 +2543,10 @@ pinctrl_handle_put_dhcpv6_opts(
             break;
         }
 
+        case DHCPV6_OPT_FQDN_CODE:
+            fqdn_flags = *(in_dhcpv6_data + sizeof *in_opt);
+            break;
+
         default:
             break;
         }
@@ -2547,7 +2570,7 @@ pinctrl_handle_put_dhcpv6_opts(
         OFPBUF_STUB_INITIALIZER(out_ofpacts_dhcpv6_opts_stub);
 
     if (!compose_out_dhcpv6_opts(userdata, &out_dhcpv6_opts,
-                                 iaid, ipxe_req)) {
+                                 iaid, ipxe_req, fqdn_flags)) {
         VLOG_WARN_RL(&rl, "Invalid userdata");
         goto exit;
     }
@@ -2762,48 +2785,16 @@ dns_build_ptr_answer(
     struct ofpbuf *dns_answer, const uint8_t *in_queryname,
     uint16_t query_length, const char *answer_data)
 {
-    char *encoded_answer;
-    uint16_t encoded_answer_length;
-
     dns_build_base_answer(dns_answer, in_queryname, query_length,
                           DNS_QUERY_TYPE_PTR);
 
-    /* Initialize string 2 chars longer than real answer:
-     * first label length and terminating zero-length label.
-     * If the answer_data is - vm1tst.ovn.org, it will be encoded as
-     *  - 0010 (Total length which is 16)
-     *  - 06766d31747374 (vm1tst)
-     *  - 036f766e (ovn)
-     *  - 036f7267 (org
-     *  - 00 (zero length field) */
-    encoded_answer_length = strlen(answer_data) + 2;
-    encoded_answer = (char *)xzalloc(encoded_answer_length);
-
-    put_be16(dns_answer, htons(encoded_answer_length));
-    uint8_t label_len_index = 0;
-    uint16_t label_len = 0;
-    char *encoded_answer_ptr = (char *)encoded_answer + 1;
-    while (*answer_data) {
-        if (*answer_data == '.') {
-            /* Label has ended.  Update the length of the label. */
-            encoded_answer[label_len_index] = label_len;
-            label_len_index += (label_len + 1);
-            label_len = 0; /* Init to 0 for the next label. */
-        } else {
-            *encoded_answer_ptr =  *answer_data;
-            label_len++;
-        }
-        encoded_answer_ptr++;
-        answer_data++;
-    }
+    size_t encoded_len = 0;
+    char *encoded = encode_fqdn_string(answer_data, &encoded_len);
 
-    /* This is required for the last label if it doesn't end with '.' */
-    if (label_len) {
-        encoded_answer[label_len_index] = label_len;
-    }
+    put_be16(dns_answer, htons(encoded_len));
+    ofpbuf_put(dns_answer, encoded, encoded_len);
 
-    ofpbuf_put(dns_answer, encoded_answer, encoded_answer_length);
-    free(encoded_answer);
+    free(encoded);
 }
 
 /* Called with in the pinctrl_handler thread context. */
diff --git a/lib/actions.c b/lib/actions.c
index ec27223f9..037172e60 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2470,7 +2470,8 @@ parse_gen_opt(struct action_context *ctx, struct 
ovnact_gen_option *o,
     }
 
     if (!strcmp(o->option->type, "str") ||
-        !strcmp(o->option->type, "domains")) {
+        !strcmp(o->option->type, "domains") ||
+        !strcmp(o->option->type, "domain")) {
         if (o->value.type != EXPR_C_STRING) {
             lexer_error(ctx->lexer, "%s option %s requires string value.",
                         opts_type, o->option->name);
@@ -2903,6 +2904,12 @@ encode_put_dhcpv6_option(const struct ovnact_gen_option 
*o,
         size = strlen(c->string);
         opt->len = htons(size);
         ofpbuf_put(ofpacts, c->string, size);
+    } else if (!strcmp(o->option->type, "domain")) {
+        char *encoded = encode_fqdn_string(c->string, &size);
+        opt->len = htons(size);
+        ofpbuf_put(ofpacts, encoded, size);
+
+        free(encoded);
     }
 }
 
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index d9b103119..9dc331421 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -270,6 +270,7 @@ BUILD_ASSERT_DECL(DHCP_OPT_HEADER_LEN == sizeof(struct 
dhcp_opt_header));
 #define DHCPV6_OPT_DOMAIN_SEARCH_CODE    24
 #define DHCPV6_OPT_IA_PD                 25
 #define DHCPV6_OPT_IA_PREFIX             26
+#define DHCPV6_OPT_FQDN_CODE             39
 #define DHCPV6_OPT_BOOT_FILE_URL         59
 #define DHCPV6_OPT_BOOT_FILE_URL_ALT    254
 
@@ -291,6 +292,15 @@ BUILD_ASSERT_DECL(DHCP_OPT_HEADER_LEN == sizeof(struct 
dhcp_opt_header));
 #define DHCPV6_OPT_BOOTFILE_NAME_ALT \
     DHCP_OPTION("bootfile_name_alt", DHCPV6_OPT_BOOT_FILE_URL_ALT, "str")
 
+#define DHCPV6_OPT_FQDN \
+    DHCP_OPTION("fqdn", DHCPV6_OPT_FQDN_CODE, "domain")
+
+/* DHCPv6 FQDN flags. RFC 4704 */
+#define DHCPV6_FQDN_FLAGS_UNDEFINED 0xff
+#define DHCPV6_FQDN_FLAGS_S 1 << 0
+#define DHCPV6_FQDN_FLAGS_O 1 << 1
+#define DHCPV6_FQDN_FLAGS_N 1 << 2
+
 OVS_PACKED(
 struct dhcpv6_opt_header {
     ovs_be16 code;
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index bffb521cf..12ca27319 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -1126,3 +1126,30 @@ void flow_collector_ids_clear(struct flow_collector_ids 
*ids)
     flow_collector_ids_destroy(ids);
     flow_collector_ids_init(ids);
 }
+
+char *
+encode_fqdn_string(const char *fqdn, size_t *len)
+{
+
+    size_t domain_len = strlen(fqdn);
+    *len = domain_len + 2;
+    char *encoded = xzalloc(*len);
+
+    int8_t label_len = 0;
+    for (size_t i = 0; i < domain_len; i++) {
+        if (fqdn[i] == '.') {
+            encoded[i - label_len] = label_len;
+            label_len = 0;
+        } else {
+            encoded[i + 1] = fqdn[i];
+            label_len++;
+        }
+    }
+
+    /* This is required for the last label if it doesn't end with '.' */
+    if (label_len) {
+        encoded[domain_len - label_len] = label_len;
+    }
+
+    return encoded;
+}
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index b17b0e236..0af940918 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -403,4 +403,9 @@ bool flow_collector_ids_lookup(const struct 
flow_collector_ids *, uint32_t);
 void flow_collector_ids_destroy(struct flow_collector_ids *);
 void flow_collector_ids_clear(struct flow_collector_ids *);
 
+/* The DNS format is 2 bytes longer than the "domain".
+ * It replaces every '.' with len of the next name.
+ * The returned pointer has to be freed by caller. */
+char *encode_fqdn_string(const char *fqdn, size_t *len);
+
 #endif /* OVN_UTIL_H */
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 3515b68a2..2bafbd4af 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -273,7 +273,8 @@ static struct gen_opts_map supported_dhcpv6_opts[] = {
     DHCPV6_OPT_DOMAIN_SEARCH,
     DHCPV6_OPT_DNS_SERVER,
     DHCPV6_OPT_BOOTFILE_NAME,
-    DHCPV6_OPT_BOOTFILE_NAME_ALT
+    DHCPV6_OPT_BOOTFILE_NAME_ALT,
+    DHCPV6_OPT_FQDN,
 };
 
 static bool
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 9afe3b584..05e155b89 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -4191,6 +4191,13 @@ or
             way. Default value for this option is false.
           </p>
         </column>
+
+        <column name="options" key="fqdn">
+          <p>
+            The DHCPv6 option code for this option is 39.
+            If set, indicates the DHCPv6 option "FQDN".
+          </p>
+        </column>
       </group>
     </group>
 
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index f59af8cc5..06e16b403 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "20.27.2",
-    "cksum": "1291808617 30462",
+    "version": "20.27.3",
+    "cksum": "3876528905 30472",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -311,7 +311,7 @@
                 "type": {
                     "type": {"key": {
                         "type": "string",
-                        "enum": ["set", ["ipv6", "str", "mac"]]}}}},
+                        "enum": ["set", ["ipv6", "str", "mac", "domain"]]}}}},
             "isRoot": true},
         "Connection": {
             "columns": {
diff --git a/tests/ovn.at b/tests/ovn.at
index 5e6a8fefa..2b8ed7571 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1697,6 +1697,9 @@ reg1[0] = 
put_dhcpv6_opts(bootfile_name="https://127.0.0.1/boot.ipxe";);
 reg1[0] = put_dhcpv6_opts(bootfile_name_alt="https://127.0.0.1/boot.ipxe";);
     formats as reg1[0] = put_dhcpv6_opts(bootfile_name_alt = 
"https://127.0.0.1/boot.ipxe";);
     encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.fe.00.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65,pause)
+reg1[0] = put_dhcpv6_opts(fqdn="ovn.org");
+    formats as reg1[0] = put_dhcpv6_opts(fqdn = "ovn.org");
+    encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.27.00.09.03.6f.76.6e.03.6f.72.67.00,pause)
 
 # lookup_nd
 reg2[0] = lookup_nd(inport, ip6.dst, eth.src);
@@ -7083,6 +7086,7 @@ AT_CLEANUP
 
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([dhcpv6 : 1 HV, 2 LS, 5 LSPs])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
 ovn_start
 
 ovn-nbctl ls-add ls1
@@ -7171,61 +7175,56 @@ trim_zeros() {
 # packet should be received twice (one from ovn-controller and the other
 # from the "ovs-ofctl monitor br-int resume"
 test_dhcpv6() {
-    local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 ipxe=$6
-    if test $ipxe -eq 2; then
-        req_len=34
-    elif test $msg_code != 0b; then
-        req_len=2a
-    else
-        req_len=1a
-    fi
-    local request=ffffffffffff${src_mac}86dd6000000000${req_len}1101${src_lla}
-    # dst ip ff02::1:2
-    request=${request}ff020000000000000000000000010002
-    # udp header and dhcpv6 header
-    request=${request}0222022300${req_len}ffff${msg_code}010203
-    # Client identifier
-    request=${request}0001000a00030001${src_mac}
+    local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5 boot_file=$6
+    local fqdn=$7
+
+    local req_scapy="Ether(dst='ff:ff:ff:ff:ff:ff', src='${src_mac}')/ \
+                     IPv6(dst='ff02::1:2', src='${src_lla}')/ \
+                     UDP(sport=546, dport=547)/ \
+                     DHCP6(msgtype=${msg_code}, trid=0x010203)/ \
+                     DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))"
+
     # Add IA-NA (Identity Association for Non Temporary Address) if msg_code
     # is not 11 (information request packet)
-    if test $msg_code != 0b; then
-        request=${request}0003000c0102030400000e1000001518
+    if test $msg_code != 11; then
+        req_scapy="${req_scapy}/DHCP6OptIA_NA(iaid=0x01020304, T1=3600, 
T2=5400)"
     fi
-    if test $ipxe -eq 2; then
-        request=${request}000f0006000669505845
+    if test "$boot_file" = "bootfile_name"; then
+        local uc_data="USER_CLASS_DATA(data=b'iPXE')"
+        
req_scapy="${req_scapy}/DHCP6OptUserClass(userclassdata=[[${uc_data}]])"
     fi
-    shift; shift; shift; shift; shift; shift;
+    if test -n "$fqdn"; then
+        req_scapy="${req_scapy}/DHCP6OptClientFQDN(flags=0x01, fqdn=b'test')"
+    fi
+    request=$(fmt_pkt "${req_scapy}")
+    shift; shift; shift; shift; shift; shift; shift;
     if test $offer_ip != 0; then
-        local server_mac=000000100001
-        local server_lla=fe80000000000000020000fffe100001
-        local reply_code=07
-        if test $msg_code = 01; then
-            reply_code=02
-        fi
-        local msg_len=54
-        if test $ipxe -eq 1; then
-            msg_len=69
-        elif test $ipxe -eq 2; then
-            msg_len=65
-        elif test $offer_ip = 1; then
-            msg_len=28
+        local reply_code=7
+        if test $msg_code = 1; then
+            reply_code=2
         fi
-        local 
reply=${src_mac}${server_mac}86dd6000000000${msg_len}1101${server_lla}${src_lla}
-        # udp header and dhcpv6 header
-        reply=${reply}0223022200${msg_len}ffff${reply_code}010203
-        # Client identifier
-        reply=${reply}0001000a00030001${src_mac}
-        # IA-NA
+
+        local rep_scapy="Ether(dst='${src_mac}', src='00:00:00:10:00:01')/ \
+                         IPv6(dst='${src_lla}', 
src='fe80::0200:00ff:fe10:0001')/ \
+                         UDP(sport=547, dport=546)/ \
+                         DHCP6(msgtype=${reply_code}, trid=0x010203)/ \
+                         DHCP6OptClientId(duid=DUID_LL(lladdr='${src_mac}'))"
+
         if test $offer_ip != 1; then
-            
reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff
+            local ip_scapy="DHCP6OptIAAddress(addr='${offer_ip}', \
+                            preflft=0xffffffff, validlft=0xffffffff)"
+            rep_scapy="${rep_scapy}/ \
+                       DHCP6OptIA_NA(iaid=0x01020304, T1=0xffffffff, \
+                       T2=0xffffffff, ianaopts=[[${ip_scapy}]])"
         fi
-        if test $ipxe -eq 1; then
-            reply=${reply}003b0011626f6f7466696c655f6e616d655f616c74
-        elif test $ipxe -eq 2; then
-            reply=${reply}003b000d626f6f7466696c655f6e616d65
+        if test -n "$boot_file"; then
+            
rep_scapy="${rep_scapy}/DHCP6OptBootFileUrl(optdata=b'${boot_file}')"
         fi
-        # Server identifier
-        reply=${reply}0002000a00030001${server_mac}
+        if test -n "$fqdn"; then
+            rep_scapy="${rep_scapy}/DHCP6OptClientFQDN(flags=0x06, 
fqdn=b'${fqdn}')"
+        fi
+        
rep_scapy="${rep_scapy}/DHCP6OptServerId(duid=DUID_LL(lladdr='00:00:00:10:00:01'))"
+        reply=$(fmt_pkt "${rep_scapy}")
         echo $reply | trim_zeros >> $inport.expected
     else
         for outport; do
@@ -7249,16 +7248,16 @@ echo "---------------------"
 ovn-sbctl list logical_flow
 echo "---------------------"
 
-ovn-sbctl dump-flows > sbflows
+#ovn-sbctl dump-flows > sbflows
 AT_CAPTURE_FILE([sbflows])
 
 echo "------ hv1 dump ----------"
 as hv1 ovs-ofctl dump-flows br-int
 
-src_mac=f00000000001
-src_lla=fe80000000000000f20000fffe000001
-offer_ip=ae700000000000000000000000000004
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip 0
+src_mac="f0:00:00:00:00:01"
+src_lla="fe80::f200:00ff:fe00:0001"
+offer_ip="ae70::4"
+test_dhcpv6 1 $src_mac $src_lla 1 $offer_ip "" ""
 
 # NXT_RESUMEs should be 1.
 OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -7281,12 +7280,12 @@ rm  1.expected
 reset_pcap_file hv1-vif1 hv1/vif1
 reset_pcap_file hv1-vif2 hv1/vif2
 
-src_mac=f00000000002
-src_lla=fe80000000000000f20000fffe000002
-offer_ip=ae700000000000000000000000000005
+src_mac="f0:00:00:00:00:02"
+src_lla="fe80::f200:00ff:fe00:0002"
+offer_ip="ae70::5"
 # Set invalid msg_type
 
-test_dhcpv6 2 $src_mac $src_lla 10 0 0 1 1
+test_dhcpv6 2 $src_mac $src_lla 16 0 "" "" 1 1
 
 # NXT_RESUMEs should be 2.
 OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -7305,9 +7304,9 @@ AT_CHECK([cat 1.packets], [0], [expout])
 # There should be no DHCPv6 reply from ovn-controller and the request packet
 # should be received by ls2-lp2.
 
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-test_dhcpv6 3 $src_mac $src_lla 01 0 0 4
+src_mac="f0:00:00:00:00:03"
+src_lla="fe80::f200:00ff:fe00:0003"
+test_dhcpv6 3 $src_mac $src_lla 1 0 "" "" 4
 
 # NXT_RESUMEs should be 2 only.
 OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -7323,10 +7322,10 @@ AT_CHECK([cat 4.packets], [0], [expout])
 
 # Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for 
this port.
 # The DHCPv6 reply shouldn't contain offer_ip.
-src_mac=f00000000022
-src_lla=fe80000000000000f20000fffe000022
+src_mac="f0:00:00:00:00:22"
+src_lla="fe80::f200:00ff:fe00:0022"
 reset_pcap_file hv1-vif5 hv1/vif5
-test_dhcpv6 5 $src_mac $src_lla 01 1 0 5
+test_dhcpv6 5 $src_mac $src_lla 1 1 "" "" 5
 
 # NXT_RESUMEs should be 3.
 OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -7338,11 +7337,11 @@ AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], 
[expout])
 
 # Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 reply
 # shouldn't contain offer_ip
-src_mac=f00000000022
-src_lla=fe80000000000000f20000fffe000022
+src_mac="f0:00:00:00:00:22"
+src_lla="fe80::f200:00ff:fe00:0022"
 reset_pcap_file hv1-vif5 hv1/vif5
 rm -f 5.expected
-test_dhcpv6 5 $src_mac $src_lla 0b 1 0 5
+test_dhcpv6 5 $src_mac $src_lla 11 1 "" "" 5
 
 # NXT_RESUMEs should be 4.
 OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -7363,11 +7362,11 @@ ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1}
 
 reset_pcap_file hv1-vif2 hv1/vif2
 
-src_mac=f00000000002
-src_lla=fe80000000000000f20000fffe000002
-offer_ip=ae700000000000000000000000000005
+src_mac="f0:00:00:00:00:02"
+src_lla="fe80::f200:00ff:fe00:0002"
+offer_ip="ae70::5"
 
-test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 1
+test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name_alt" ""
 # NXT_RESUMEs should be 5.
 OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
@@ -7381,7 +7380,7 @@ AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], 
[expout])
 reset_pcap_file hv1-vif2 hv1/vif2
 rm 2.packets 2.expected
 
-test_dhcpv6 2 $src_mac $src_lla 01 $offer_ip 2
+test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "bootfile_name" ""
 # NXT_RESUMEs should be 6.
 OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
@@ -7389,6 +7388,24 @@ $PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" 
hv1/vif2-tx.pcap | trim_zeros > 2.pa
 cat 2.expected | cut -c 1-120,125- > expout
 AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout])
 
+ovn-nbctl --all destroy dhcp-option
+d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64")"
+ovn-nbctl dhcp-options-set-options $d1 \
+    server_id=00:00:00:10:00:01 \
+    fqdn=\"ovn.org\"
+ovn-nbctl --wait=hv lsp-set-dhcpv6-options ls1-lp2 ${d1}
+
+reset_pcap_file hv1-vif2 hv1/vif2
+rm 2.packets 2.expected
+
+test_dhcpv6 2 $src_mac $src_lla 1 $offer_ip "" "ovn.org"
+# NXT_RESUMEs should be 7.
+OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 
2.packets
+cat 2.expected | cut -c 1-120,125- > expout
+AT_CHECK([cat 2.packets | cut -c 1-120,125- ], [0], [expout])
+
 OVN_CLEANUP([hv1])
 
 AT_CLEANUP
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index ce9213c1d..6c7754eac 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -207,6 +207,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap 
*dhcpv6_opts,
     dhcp_opt_add(dhcpv6_opts, "domain_search",  24, "str");
     dhcp_opt_add(dhcpv6_opts, "bootfile_name", 59, "str");
     dhcp_opt_add(dhcpv6_opts, "bootfile_name_alt", 254, "str");
+    dhcp_opt_add(dhcpv6_opts, "fqdn", 39, "domain");
 
     /* IPv6 ND RA options. */
     hmap_init(nd_ra_opts);
-- 
2.40.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to