Add an "ia_pd" DHCPv6 option that lets the CMS supply a delegated prefix in a
logical port's DHCP_Options for OVN's native DHCPv6 server to hand out, with no
pool or lease state kept in the dataplane.  This lets OVN delegate a prefix
(for example to a VM that in turn sub-delegates to its own workloads) while
remaining the stateless responder it already is for IA_NA.

The server is request-aware per RFC 8415: it parses the client's IA_PD,
captures its IAID, and emits an IA_PD -> IA_PREFIX carrying the configured
prefix only when the client actually solicited one, echoing the client's IAID.
Prefixes are advertised with infinite lifetimes, matching the existing IA_NA
emit.  Only Solicit and Request elicit a delegated prefix; an IA_PD in Confirm,
Decline or Information-request is ignored, and only the first IA_PD from a
client is honored (a single prefix is delegated).

A port must also receive a managed IPv6 address (ia_addr), which is what makes
northd intercept its DHCPv6 traffic; PD-only ports are not yet supported.

  - lib/ovn-l7.h, northd/ovn-northd.c: register DHCPV6_OPT_IA_PD_PREFIX
    ("ia_pd", code 25, type "ia_pd") and add it to supported_dhcpv6_opts[].
  - lib/actions.c: encode the "ia_pd" type into the action buffer as
    [plen][prefix], deriving plen from the value's mask.  parse_gen_opt
    rejects a value that is not a single masked IPv6 prefix, since the mask
    supplies the on-wire prefix length (a bare address would silently encode
    a /0, and a set would drop all but the first).
  - controller/pinctrl.c: parse the client IA_PD and emit the delegated
    prefix, copying it with memcpy to avoid an unaligned struct in6_addr
    access on strict-alignment architectures.
  - ovn-sb.ovsschema: allow "ia_pd" in the DHCPv6_Options "type" column enum
    and bump the schema version.  northd writes the supported option into the
    Southbound DHCPv6_Options table, so without this every northd SB commit
    failed with a constraint violation.
  - ovn-nb.xml, ovn-sb.xml: document the new option and the SB type.
  - tests: register ia_pd in the test harness, add put_dhcpv6_opts parse and
    encode cases, and extend the dhcpv6 system test to exercise the
    request-aware path (a sibling IA_NA-only port must not receive an IA_PD).

Signed-off-by: Dax Kelson <[email protected]>
---
 controller/pinctrl.c | 84 +++++++++++++++++++++++++++++++++++++++++---
 lib/actions.c        | 20 +++++++++++
 lib/ovn-l7.h         |  3 ++
 northd/ovn-northd.c  |  1 +
 ovn-nb.xml           | 33 +++++++++++++++++
 ovn-sb.ovsschema     |  7 ++--
 ovn-sb.xml           | 18 ++++++++++
 tests/ovn.at         | 68 ++++++++++++++++++++++++++++++++++-
 tests/test-ovn.c     |  1 +
 9 files changed, 227 insertions(+), 8 deletions(-)

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index fce1c4be0..ff5004746 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -2962,7 +2962,8 @@ encode_dhcpv6_server_id_opt(struct ofpbuf *opts, void 
*user_data)
 static bool
 compose_out_dhcpv6_opts(struct ofpbuf *userdata,
                         struct ofpbuf *out_dhcpv6_opts,
-                        ovs_be32 iaid, bool ipxe_req, uint8_t fqdn_flags)
+                        ovs_be32 iaid, bool ipxe_req, uint8_t fqdn_flags,
+                        ovs_be32 pd_iaid, bool pd_requested)
 {
     while (userdata->size) {
         struct dhcpv6_opt_header *userdata_opt = ofpbuf_try_pull(
@@ -3025,6 +3026,54 @@ compose_out_dhcpv6_opts(struct ofpbuf *userdata,
             break;
         }
 
+        case DHCPV6_OPT_IA_PD:
+        {
+            if (size != sizeof(uint8_t) + sizeof(struct in6_addr)) {
+                return false;
+            }
+
+            if (!pd_requested) {
+                /* The client did not ask for a delegated prefix, so don't
+                 * put an IA_PD option in the response. */
+                break;
+            }
+
+            /* The IA Prefix option carries a delegated prefix and must be
+             * encapsulated in the Options field of an IA_PD option.  Please
+             * see RFC 8415 sections 21.21 and 21.22. */
+            uint8_t plen = *(uint8_t *) userdata_opt_data;
+
+            struct dhcpv6_opt_ia_na *opt_ia_pd = ofpbuf_put_zeros(
+                out_dhcpv6_opts, sizeof *opt_ia_pd);
+            opt_ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
+            /* IA_PD length (in bytes)-
+             *  IAID - 4
+             *  T1   - 4
+             *  T2   - 4
+             *  IA Prefix - sizeof(struct dhcpv6_opt_ia_prefix)
+             */
+            opt_ia_pd->opt.len =
+                htons(12 + sizeof(struct dhcpv6_opt_ia_prefix));
+            opt_ia_pd->iaid = pd_iaid;
+            /* Set the lifetime of the prefix to infinity. */
+            opt_ia_pd->t1 = OVS_BE32_MAX;
+            opt_ia_pd->t2 = OVS_BE32_MAX;
+
+            struct dhcpv6_opt_ia_prefix *opt_ia_prefix = ofpbuf_put_zeros(
+                out_dhcpv6_opts, sizeof *opt_ia_prefix);
+            opt_ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
+            opt_ia_prefix->opt.len =
+                htons(sizeof(struct dhcpv6_opt_ia_prefix) -
+                      DHCP6_OPT_HEADER_LEN);
+            opt_ia_prefix->plife_time = OVS_BE32_MAX;
+            opt_ia_prefix->vlife_time = OVS_BE32_MAX;
+            opt_ia_prefix->plen = plen;
+            memcpy(&opt_ia_prefix->ipv6,
+                   (uint8_t *) userdata_opt_data + sizeof plen,
+                   sizeof opt_ia_prefix->ipv6);
+            break;
+        }
+
         case DHCPV6_OPT_DNS_SERVER_CODE:
         {
             struct dhcpv6_opt_header *opt_dns = ofpbuf_put_zeros(
@@ -3209,6 +3258,8 @@ pinctrl_handle_put_dhcpv6_opts(
      * option. So we don't need to copy that in the Server's response.
      * */
     ovs_be32 iaid = 0;
+    ovs_be32 pd_iaid = 0;
+    bool pd_requested = false;
     struct dhcpv6_opt_header const *in_opt_client_id = NULL;
     bool ipxe_req = false;
     uint8_t fqdn_flags = DHCPV6_FQDN_FLAGS_UNDEFINED;
@@ -3234,6 +3285,23 @@ pinctrl_handle_put_dhcpv6_opts(
             break;
         }
 
+        case DHCPV6_OPT_IA_PD:
+        {
+            if (opt_len < sizeof(struct dhcpv6_opt_ia_na)) {
+                break;
+            }
+
+            /* IA_PD shares the IAID/T1/T2 header layout with IA_NA.  OVN
+             * delegates a single prefix, so honor only the first IA_PD. */
+            if (!pd_requested) {
+                struct dhcpv6_opt_ia_na *opt_ia_pd =
+                    (struct dhcpv6_opt_ia_na *) in_opt;
+                pd_iaid = opt_ia_pd->iaid;
+                pd_requested = true;
+            }
+            break;
+        }
+
         case DHCPV6_OPT_CLIENT_ID_CODE:
             in_opt_client_id = in_opt;
             break;
@@ -3271,8 +3339,15 @@ pinctrl_handle_put_dhcpv6_opts(
         goto exit;
     }
 
-    if (!iaid && hdr->msg_type != DHCPV6_MSG_TYPE_INFO_REQ) {
-        VLOG_WARN_RL(&rl, "DHCPv6 option - IA NA not present in the "
+    /* A delegated prefix is only offered in reply to a Solicit or Request. */
+    if (hdr->msg_type != DHCPV6_MSG_TYPE_SOLICIT &&
+        hdr->msg_type != DHCPV6_MSG_TYPE_REQUEST) {
+        pd_requested = false;
+    }
+
+    if (!iaid && !pd_requested &&
+        hdr->msg_type != DHCPV6_MSG_TYPE_INFO_REQ) {
+        VLOG_WARN_RL(&rl, "DHCPv6 option - no IA_NA or IA_PD present in the "
                      "DHCPv6 packet");
         goto exit;
     }
@@ -3286,7 +3361,8 @@ pinctrl_handle_put_dhcpv6_opts(
         compose = compose_dhcpv6_status(userdata, &out_dhcpv6_opts);
     } else {
         compose = compose_out_dhcpv6_opts(userdata, &out_dhcpv6_opts, iaid,
-                                          ipxe_req, fqdn_flags);
+                                          ipxe_req, fqdn_flags, pd_iaid,
+                                          pd_requested);
     }
 
     if (!compose) {
diff --git a/lib/actions.c b/lib/actions.c
index 0367b330c..725674b0c 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2694,6 +2694,20 @@ parse_gen_opt(struct action_context *ctx, struct 
ovnact_gen_option *o,
             return;
         }
     }
+
+    /* The ia_pd value is a single IPv6 prefix: its mask supplies the
+     * delegated prefix length on the wire, so reject a bare address (which
+     * would encode a meaningless /0) or a set of addresses. */
+    if (!strcmp(o->option->type, "ia_pd")) {
+        const struct expr_constant *c = vector_get_ptr(&o->value.values, 0);
+        if (vector_len(&o->value.values) != 1 ||
+            c->format != LEX_F_IPV6 || !c->masked) {
+            lexer_error(ctx->lexer, "%s option %s requires a single IPv6 "
+                        "prefix value, e.g. aef0:1::/64.",
+                        opts_type, o->option->name);
+            return;
+        }
+    }
 }
 
 static const struct ovnact_gen_option *
@@ -3221,6 +3235,12 @@ encode_put_dhcpv6_option(const struct ovnact_gen_option 
*o,
         ofpbuf_put(ofpacts, encoded, size);
 
         free(encoded);
+    } else if (!strcmp(o->option->type, "ia_pd")) {
+        uint8_t plen = ipv6_count_cidr_bits(&c->mask.ipv6);
+        size = sizeof plen + sizeof c->value.ipv6;
+        opt->len = htons(size);
+        ofpbuf_put(ofpacts, &plen, sizeof plen);
+        ofpbuf_put(ofpacts, &c->value.ipv6, sizeof c->value.ipv6);
     }
 }
 
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 5f52e1791..b23e3ca29 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -286,6 +286,9 @@ BUILD_ASSERT_DECL(DHCP_OPT_HEADER_LEN == sizeof(struct 
dhcp_opt_header));
 #define DHCPV6_OPT_IA_ADDR  \
     DHCP_OPTION("ia_addr", DHCPV6_OPT_IA_ADDR_CODE, "ipv6")
 
+#define DHCPV6_OPT_IA_PD_PREFIX  \
+    DHCP_OPTION("ia_pd", DHCPV6_OPT_IA_PD, "ia_pd")
+
 #define DHCPV6_OPT_DNS_SERVER  \
     DHCP_OPTION("dns_server", DHCPV6_OPT_DNS_SERVER_CODE, "ipv6")
 
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index c4c71ca81..e5ea2fdf7 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -284,6 +284,7 @@ static struct gen_opts_map supported_dhcp_opts[] = {
 
 static struct gen_opts_map supported_dhcpv6_opts[] = {
     DHCPV6_OPT_IA_ADDR,
+    DHCPV6_OPT_IA_PD_PREFIX,
     DHCPV6_OPT_SERVER_ID,
     DHCPV6_OPT_DOMAIN_SEARCH,
     DHCPV6_OPT_DNS_SERVER,
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 15fb1d7e8..8622267e1 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -5918,6 +5918,39 @@ or
           </p>
         </column>
       </group>
+
+      <group title="Prefix Delegation DHCPv6 options">
+        <p>
+          The option below makes OVN delegate an IPv6 prefix to a client that
+          requests one.  OVN does not manage a prefix pool or any lease state;
+          the CMS supplies the prefix to delegate, and OVN emits it only when
+          the client solicits a prefix.
+        </p>
+
+        <column name="options" key="ia_pd">
+          <p>
+            The DHCPv6 option code for this option is 25, ``Identity
+            Association for Prefix Delegation'' (IA_PD), which carries an IA
+            Prefix (option 26).  The value is the delegated prefix in CIDR
+            notation, e.g. <code>aef0:1::/64</code>.  When the client requests
+            an IA_PD, OVN replies with this prefix and echoes the client's
+            IA_PD IAID.  Please refer to RFC 8415 for more details.
+          </p>
+
+          <p>
+            A single prefix is delegated: only the first IA_PD a client
+            requests is answered.  If this option is not set, no IA_PD is
+            returned even when the client requests one, the same way a missing
+            <code>ia_addr</code> is handled.
+          </p>
+
+          <p>
+            The logical port must also receive a DHCPv6-managed IPv6 address
+            (see <code>ia_addr</code>), which is what makes OVN intercept the
+            port's DHCPv6 traffic.
+          </p>
+        </column>
+      </group>
     </group>
 
     <group title="Common Columns">
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index 3d79a64b4..40bdf9b32 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "21.10.0",
-    "cksum": "2298996353 36780",
+    "version": "21.11.0",
+    "cksum": "2650041963 36830",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -324,7 +324,8 @@
                 "type": {
                     "type": {"key": {
                         "type": "string",
-                        "enum": ["set", ["ipv6", "str", "mac", "domain"]]}}}},
+                        "enum": ["set", ["ipv6", "str", "mac", "domain",
+                                         "ia_pd"]]}}}},
             "isRoot": true},
         "Connection": {
             "columns": {
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 0d3ec6c2c..4069acfc0 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -4440,6 +4440,24 @@ tcp.flags = RST;
             put_dhcpv6_opts(..., server_id = 01:02:03:04L05:06,...)
           </p>
         </dd>
+
+        <dt><code>value: ia_pd</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCPv6 option is a single IPv6
+            prefix to be delegated to the client via Prefix Delegation
+            (IA_PD/IA_PREFIX).  The value must be a masked IPv6 address; the
+            mask supplies the delegated prefix length.
+          </p>
+
+          <p>
+            Example. "name=ia_pd", "code=25", "type=ia_pd".
+          </p>
+
+          <p>
+            put_dhcpv6_opts(..., ia_pd = aef0:1::/64,...)
+          </p>
+        </dd>
       </dl>
     </column>
   </table>
diff --git a/tests/ovn.at b/tests/ovn.at
index 5dda50044..9ae023577 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1625,6 +1625,10 @@ put_nd(inport, nd.target, nd.sll);
 # put_dhcpv6_opts
 reg1[[0]] = put_dhcpv6_opts(ia_addr = ae70::4, server_id = 00:00:00:00:10:02);
     encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.05.00.10.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.04.00.02.00.06.00.00.00.00.10.02,pause)
+reg1[[0]] = put_dhcpv6_opts(ia_pd = aef0:1::/64);
+    encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.19.00.11.40.ae.f0.00.01.00.00.00.00.00.00.00.00.00.00.00.00,pause)
+reg1[[0]] = put_dhcpv6_opts(ia_addr = ae70::4, ia_pd = aef0:1::/64, server_id 
= 00:00:00:00:10:02);
+    encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.05.00.10.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.04.00.19.00.11.40.ae.f0.00.01.00.00.00.00.00.00.00.00.00.00.00.00.00.02.00.06.00.00.00.00.10.02,pause)
 reg1[[0]] = put_dhcpv6_opts();
     encodes as 
controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40,pause)
 reg1[[0]] = put_dhcpv6_opts(dns_server={ae70::1,ae70::2});
@@ -1645,6 +1649,14 @@ reg1[[0]] = put_dhcpv6_opts(ia_addr="ae70::4");
     DHCPv6 option ia_addr requires numeric value.
 reg1[[0]] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1);
     DHCPv6 option domain_search requires string value.
+reg1[[0]] = put_dhcpv6_opts(ia_pd = "aef0:1::/64");
+    DHCPv6 option ia_pd requires numeric value.
+reg1[[0]] = put_dhcpv6_opts(ia_pd = aef0:1::);
+    DHCPv6 option ia_pd requires a single IPv6 prefix value, e.g. aef0:1::/64.
+reg1[[0]] = put_dhcpv6_opts(ia_pd = {aef0:1::/64, aef0:2::/64});
+    DHCPv6 option ia_pd requires a single IPv6 prefix value, e.g. aef0:1::/64.
+reg1[[0]] = put_dhcpv6_opts(ia_pd = 1.2.3.4/24);
+    DHCPv6 option ia_pd requires a single IPv6 prefix value, e.g. aef0:1::/64.
 reg1[[0]] = put_dhcpv6_opts(bootfile_name="https://127.0.0.1/boot.ipxe";);
     formats as reg1[[0]] = put_dhcpv6_opts(bootfile_name = 
"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.3b.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)
@@ -7623,7 +7635,7 @@ check ovn-nbctl lsp-add ls1 ls1-lp3 \
 check ovn-nbctl lsp-set-port-security ls1-lp3 "f0:00:00:00:00:22 ae70::22"
 
 d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \
-options="\"server_id\"=\"00:00:00:10:00:01\"")"
+options="\"server_id\"=\"00:00:00:10:00:01\" \"ia_pd\"=\"aef0:1::/64\"")"
 
 check ovn-nbctl lsp-set-dhcpv6-options ls1-lp1 ${d1}
 check ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1}
@@ -7783,6 +7795,51 @@ test_dhcpv6_release() {
     n_resume=$((n_resume + 1))
 }
 
+# This shell function sends a DHCPv6 request that carries an IA_PD option and
+# checks that the reply delegates a prefix in an IA_PD -> IA_PREFIX.  The
+# request's IA_PD uses a distinct IAID so the reply must echo the IA_PD IAID,
+# not the IA_NA one.
+# test_dhcpv6_pd INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP PD_PREFIX 
PD_PLEN
+test_dhcpv6_pd() {
+    local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5
+    local pd_prefix=$6 pd_plen=$7
+
+    local req_scapy="Ether(dst='33:33:00:01:00:02', 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}'))/ \
+                     DHCP6OptIA_NA(iaid=0x01020304, T1=3600, T2=5400)/ \
+                     DHCP6OptIA_PD(iaid=0x0a0b0c0d, T1=3600, T2=5400)"
+    request=$(fmt_pkt "${req_scapy}")
+
+    local reply_code=7
+    if test $msg_code = 1; then
+        reply_code=2
+    fi
+
+    local ip_scapy="DHCP6OptIAAddress(addr='${offer_ip}', \
+                    preflft=0xffffffff, validlft=0xffffffff)"
+    local pd_scapy="DHCP6OptIAPrefix(prefix='${pd_prefix}', plen=${pd_plen}, \
+                    preflft=0xffffffff, validlft=0xffffffff)"
+    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}'))/ \
+                     DHCP6OptIA_NA(iaid=0x01020304, T1=0xffffffff, \
+                     T2=0xffffffff, ianaopts=[[${ip_scapy}]])/ \
+                     DHCP6OptIA_PD(iaid=0x0a0b0c0d, T1=0xffffffff, \
+                     T2=0xffffffff, iapdopt=[[${pd_scapy}]])/ \
+                     
DHCP6OptServerId(duid=DUID_LL(lladdr='00:00:00:10:00:01'))"
+    reply=$(fmt_pkt "${rep_scapy}")
+    echo $reply | trim_zeros >> $inport.expected
+
+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
+    OVS_WAIT_UNTIL([test $n_resume = `cat ofctl_monitor*.log | grep -c 
NXT_RESUME`])
+    n_resume=$((n_resume + 1))
+}
+
 check_packets() {
     local port=$1
 
@@ -7845,6 +7902,15 @@ reset_pcap_file hv1-vif1 hv1/vif1
 test_dhcpv6_release 1 $src_mac $src_lla $offer_ip
 check_packets 1
 
+# Request a delegated prefix on ls1-lp2.  d1 carries an ia_pd option, so the
+# reply must include an IA_PD -> IA_PREFIX with the delegated prefix and the
+# client's IA_PD IAID.  The IA_NA-only request on ls1-lp1 above must NOT get an
+# IA_PD, which exercises the request-aware path.
+reset_pcap_file hv1-vif2 hv1/vif2
+test_dhcpv6_pd 2 f0:00:00:00:00:02 fe80::f200:00ff:fe00:0002 1 ae70::5 
aef0:1:: 64
+check_packets 2
+reset_pcap_file hv1-vif2 hv1/vif2
+
 # Send invalid packet on ls1-lp2. ovn-controller should resume the packet
 # without any modifications and the packet should be received by ls1-lp1.
 # ls1-lp1 will receive the packet twice, one from the ovn-controller after the
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 51471750b..460035b7f 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -203,6 +203,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap 
*dhcpv6_opts,
     hmap_init(dhcpv6_opts);
     dhcp_opt_add(dhcpv6_opts, "server_id",  2, "mac");
     dhcp_opt_add(dhcpv6_opts, "ia_addr",  5, "ipv6");
+    dhcp_opt_add(dhcpv6_opts, "ia_pd",  25, "ia_pd");
     dhcp_opt_add(dhcpv6_opts, "dns_server",  23, "ipv6");
     dhcp_opt_add(dhcpv6_opts, "domain_search",  24, "str");
     dhcp_opt_add(dhcpv6_opts, "bootfile_name", 59, "str");
-- 
2.50.1 (Apple Git-155)

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to