diff mbox series

[ovs-dev,v3] dhcp: add iPXE support to OVN

Message ID a620491b4c734f80077e94d389e20585b71f0ab3.1603448039.git.lorenzo.bianconi@redhat.com
State Accepted
Headers show
Series [ovs-dev,v3] dhcp: add iPXE support to OVN | expand

Commit Message

Lorenzo Bianconi Oct. 23, 2020, 10:27 a.m. UTC
Add iPXE support to OVN introducing "bootfile_name_alt" dhcp option.
"bootfile_name_alt" dhcp userdata is encoded as option 254 since
it is not currently used.
When both "bootfile_name" and "bootfile_name_alt" are provided
by the CMS, "bootfile_name" will be used for option 67 if the
dhcp request contains etherboot option (175), otherwise
"bootfile_name_alt" will be used.
"bootfile_name" and "bootfile_name_alt" are placed just after offer_ip
in userdata buffer.

Tested-by: Lucas Alvares Gomes Martins <lmartins@redhat.com>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
Changes since v2:
- encode "bootfile_name" and "bootfile_name_alt" are placed just after
  offer_ip in userdata buffer in order to avoid loops in
  pinctrl_handle_put_dhcp_opts()
Changes since v1:
- encode bootfile_name_alt option as 254 in order to not increase userdata
  buffer size
---
 controller/pinctrl.c |  30 ++++++++++++
 lib/actions.c        |  33 ++++++++++---
 lib/ovn-l7.h         |   7 +++
 northd/ovn-northd.c  |   1 +
 ovn-nb.xml           |  10 ++++
 tests/ovn.at         | 110 ++++++++++++++++++++++++++++++-------------
 tests/test-ovn.c     |   1 +
 7 files changed, 154 insertions(+), 38 deletions(-)

Comments

Numan Siddique Oct. 27, 2020, 2:10 p.m. UTC | #1
On Fri, Oct 23, 2020 at 4:04 PM Lorenzo Bianconi
<lorenzo.bianconi@redhat.com> wrote:
>
> Add iPXE support to OVN introducing "bootfile_name_alt" dhcp option.
> "bootfile_name_alt" dhcp userdata is encoded as option 254 since
> it is not currently used.
> When both "bootfile_name" and "bootfile_name_alt" are provided
> by the CMS, "bootfile_name" will be used for option 67 if the
> dhcp request contains etherboot option (175), otherwise
> "bootfile_name_alt" will be used.
> "bootfile_name" and "bootfile_name_alt" are placed just after offer_ip
> in userdata buffer.
>
> Tested-by: Lucas Alvares Gomes Martins <lmartins@redhat.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>

Thanks Lorenzo.

I did the below changes and applied to master.

**********

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 6f5499f0b..f15afc54f 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -2024,16 +2024,16 @@ pinctrl_handle_put_dhcp_opts(
      */
     struct dhcp_opt_header *in_dhcp_opt =
         (struct dhcp_opt_header *)reply_dhcp_opts_ptr->data;
-    if (in_dhcp_opt->code == 67) { /* DHCP_OPT_BOOTFILENAME */
+    if (in_dhcp_opt->code == DHCP_OPT_BOOTFILE_CODE) {
         unsigned char *ptr = (unsigned char *)in_dhcp_opt;
         int len = sizeof *in_dhcp_opt + in_dhcp_opt->len;
         struct dhcp_opt_header *next_dhcp_opt =
             (struct dhcp_opt_header *)(ptr + len);

-        if (next_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
+        if (next_dhcp_opt->code == DHCP_OPT_BOOTFILE_ALT_CODE) {
             if (!ipxe_req) {
                 ofpbuf_pull(reply_dhcp_opts_ptr, len);
-                next_dhcp_opt->code = 67;
+                next_dhcp_opt->code = DHCP_OPT_BOOTFILE_CODE;
             } else {
                 char *buf = xmalloc(len);

@@ -2044,8 +2044,8 @@ pinctrl_handle_put_dhcp_opts(
                 free(buf);
             }
         }
-    } else if (in_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
-        in_dhcp_opt->code = 67;
+    } else if (in_dhcp_opt->code == DHCP_OPT_BOOTFILE_ALT_CODE) {
+        in_dhcp_opt->code = DHCP_OPT_BOOTFILE_CODE;
     }

     uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
diff --git a/lib/actions.c b/lib/actions.c
index b0d03bc96..23e54ef2a 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2542,9 +2542,9 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
     ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
     ofpbuf_put(ofpacts, &offerip, sizeof offerip);

-    /* Encode and bootfile_name opt (67) */
-    const struct ovnact_gen_option *boot_opt = find_opt(
-            pdo->options, pdo->n_options, 67);
+    /* Encode bootfile_name opt (67) */
+    const struct ovnact_gen_option *boot_opt =
+        find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
     if (boot_opt) {
         uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
         const union expr_constant *c = boot_opt->value.values;
@@ -2553,8 +2553,8 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
         ofpbuf_put(ofpacts, c->string, opt_header[1]);
     }
     /* Encode bootfile_name_alt opt (254) */
-    const struct ovnact_gen_option *boot_alt_opt = find_opt(
-            pdo->options, pdo->n_options, 254);
+    const struct ovnact_gen_option *boot_alt_opt =
+        find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_ALT_CODE);
     if (boot_alt_opt) {
         uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
         const union expr_constant *c = boot_alt_opt->value.values;
@@ -2565,7 +2565,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,

     for (const struct ovnact_gen_option *o = pdo->options;
          o < &pdo->options[pdo->n_options]; o++) {
-        if (o != offerip_opt && o != boot_alt_opt && o != boot_opt) {
+        if (o != offerip_opt && o != boot_opt && o != boot_alt_opt) {
             encode_put_dhcpv4_option(o, ofpacts);
         }
     }
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index fb0088582..c3e8fd660 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -93,10 +93,14 @@ struct gen_opts_map {
 #define DHCP_OPT_DOMAIN_SEARCH_LIST \
     DHCP_OPTION("domain_search_list", 119, "domains")

-/* let's use unused 254 option for iPXE bootfile_name_alt
- * userdata dhcp option
+#define DHCP_OPT_BOOTFILE_CODE 67
+
+/* Use unused 254 option for iPXE bootfile_name_alt userdata DHCP option.
+ * This option code is replaced by 67 when sending the DHCP reply.
  */
-#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", 254, "str")
+#define DHCP_OPT_BOOTFILE_ALT_CODE 254
+#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", \
+                                          DHCP_OPT_BOOTFILE_ALT_CODE, "str")

 #define DHCP_OPT_ETHERBOOT     175


*********

> ---
> Changes since v2:
> - encode "bootfile_name" and "bootfile_name_alt" are placed just after
>   offer_ip in userdata buffer in order to avoid loops in
>   pinctrl_handle_put_dhcp_opts()
> Changes since v1:
> - encode bootfile_name_alt option as 254 in order to not increase userdata
>   buffer size
> ---
>  controller/pinctrl.c |  30 ++++++++++++
>  lib/actions.c        |  33 ++++++++++---
>  lib/ovn-l7.h         |   7 +++
>  northd/ovn-northd.c  |   1 +
>  ovn-nb.xml           |  10 ++++
>  tests/ovn.at         | 110 ++++++++++++++++++++++++++++++-------------
>  tests/test-ovn.c     |   1 +
>  7 files changed, 154 insertions(+), 38 deletions(-)
>
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index d85ba504d..6f5499f0b 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -1868,6 +1868,7 @@ pinctrl_handle_put_dhcp_opts(
>      }
>      in_dhcp_ptr += sizeof magic_cookie;
>
> +    bool ipxe_req = false;
>      const uint8_t *in_dhcp_msg_type = NULL;
>      ovs_be32 request_ip = in_dhcp_data->ciaddr;
>      while (in_dhcp_ptr < end) {
> @@ -1900,6 +1901,9 @@ pinctrl_handle_put_dhcp_opts(
>                  request_ip = get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
>              }
>              break;
> +        case DHCP_OPT_ETHERBOOT:
> +            ipxe_req = true;
> +            break;
>          default:
>              break;
>          }
> @@ -2018,6 +2022,32 @@ pinctrl_handle_put_dhcp_opts(
>       *| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding|
>       * --------------------------------------------------------------
>       */
> +    struct dhcp_opt_header *in_dhcp_opt =
> +        (struct dhcp_opt_header *)reply_dhcp_opts_ptr->data;
> +    if (in_dhcp_opt->code == 67) { /* DHCP_OPT_BOOTFILENAME */
> +        unsigned char *ptr = (unsigned char *)in_dhcp_opt;
> +        int len = sizeof *in_dhcp_opt + in_dhcp_opt->len;
> +        struct dhcp_opt_header *next_dhcp_opt =
> +            (struct dhcp_opt_header *)(ptr + len);
> +
> +        if (next_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> +            if (!ipxe_req) {
> +                ofpbuf_pull(reply_dhcp_opts_ptr, len);
> +                next_dhcp_opt->code = 67;
> +            } else {
> +                char *buf = xmalloc(len);
> +
> +                memcpy(buf, in_dhcp_opt, len);
> +                ofpbuf_pull(reply_dhcp_opts_ptr,
> +                            sizeof *in_dhcp_opt + next_dhcp_opt->len);
> +                memcpy(reply_dhcp_opts_ptr->data, buf, len);
> +                free(buf);
> +            }
> +        }
> +    } else if (in_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> +        in_dhcp_opt->code = 67;
> +    }
> +
>      uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
>      if (msg_type != DHCP_MSG_NAK) {
>          new_l4_size += reply_dhcp_opts_ptr->size;
> diff --git a/lib/actions.c b/lib/actions.c
> index 1e1bdeff2..b0d03bc96 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -2086,10 +2086,10 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
>  }
>
>  static const struct ovnact_gen_option *
> -find_offerip(const struct ovnact_gen_option *options, size_t n)
> +find_opt(const struct ovnact_gen_option *options, size_t n, size_t code)
>  {
>      for (const struct ovnact_gen_option *o = options; o < &options[n]; o++) {
> -        if (o->option->code == 0) {
> +        if (o->option->code == code) {
>              return o;
>          }
>      }
> @@ -2288,7 +2288,7 @@ parse_put_dhcp_opts(struct action_context *ctx,
>      parse_put_opts(ctx, dst, po, dhcp_opts, opts_type);
>
>      if (!ctx->lexer->error && po->ovnact.type == OVNACT_PUT_DHCPV4_OPTS
> -        && !find_offerip(po->options, po->n_options)) {
> +        && !find_opt(po->options, po->n_options, 0)) {
>          lexer_error(ctx->lexer,
>                      "put_dhcp_opts requires offerip to be specified.");
>          return;
> @@ -2537,14 +2537,35 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
>      /* Encode the offerip option first, because it's a special case and needs
>       * to be first in the actual DHCP response, and then encode the rest
>       * (skipping offerip the second time around). */
> -    const struct ovnact_gen_option *offerip_opt = find_offerip(
> -        pdo->options, pdo->n_options);
> +    const struct ovnact_gen_option *offerip_opt = find_opt(
> +        pdo->options, pdo->n_options, 0);
>      ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
>      ofpbuf_put(ofpacts, &offerip, sizeof offerip);
>
> +    /* Encode and bootfile_name opt (67) */
> +    const struct ovnact_gen_option *boot_opt = find_opt(
> +            pdo->options, pdo->n_options, 67);
> +    if (boot_opt) {
> +        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> +        const union expr_constant *c = boot_opt->value.values;
> +        opt_header[0] = boot_opt->option->code;
> +        opt_header[1] = strlen(c->string);
> +        ofpbuf_put(ofpacts, c->string, opt_header[1]);
> +    }
> +    /* Encode bootfile_name_alt opt (254) */
> +    const struct ovnact_gen_option *boot_alt_opt = find_opt(
> +            pdo->options, pdo->n_options, 254);
> +    if (boot_alt_opt) {
> +        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> +        const union expr_constant *c = boot_alt_opt->value.values;
> +        opt_header[0] = boot_alt_opt->option->code;
> +        opt_header[1] = strlen(c->string);
> +        ofpbuf_put(ofpacts, c->string, opt_header[1]);
> +    }
> +
>      for (const struct ovnact_gen_option *o = pdo->options;
>           o < &pdo->options[pdo->n_options]; o++) {
> -        if (o != offerip_opt) {
> +        if (o != offerip_opt && o != boot_alt_opt && o != boot_opt) {
>              encode_put_dhcpv4_option(o, ofpacts);
>          }
>      }
> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> index 30a795531..a2671ddd2 100644
> --- a/lib/ovn-l7.h
> +++ b/lib/ovn-l7.h
> @@ -93,6 +93,13 @@ struct gen_opts_map {
>  #define DHCP_OPT_DOMAIN_SEARCH_LIST \
>      DHCP_OPTION("domain_search_list", 119, "domains")
>
> +/* let's use unused 254 option for iPXE bootfile_name_alt
> + * userdata dhcp option
> + */
> +#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", 254, "str")
> +
> +#define DHCP_OPT_ETHERBOOT     175
> +
>  #define DHCP_OPT_ARP_CACHE_TIMEOUT \
>      DHCP_OPTION("arp_cache_timeout", 35, "uint32")
>  #define DHCP_OPT_TCP_KEEPALIVE_INTERVAL \
> diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> index 5b76868df..a3cdf8afc 100644
> --- a/northd/ovn-northd.c
> +++ b/northd/ovn-northd.c
> @@ -12472,6 +12472,7 @@ static struct gen_opts_map supported_dhcp_opts[] = {
>      DHCP_OPT_ARP_CACHE_TIMEOUT,
>      DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
>      DHCP_OPT_DOMAIN_SEARCH_LIST,
> +    DHCP_OPT_BOOTFILE_ALT,
>  };
>
>  static struct gen_opts_map supported_dhcpv6_opts[] = {
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index 86195af34..43694535e 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -3096,6 +3096,16 @@
>              resolving hostnames via the Domain Name System.
>            </p>
>          </column>
> +
> +        <column name="options" key="bootfile_name_alt">
> +          <p>
> +          </p>
> +            "bootfile_name_alt" option is used to support iPXE.
> +            When both "bootfile_name" and "bootfile_name_alt" are provided
> +            by the CMS, "bootfile_name" will be used for option 67 if the
> +            dhcp request contains etherboot option (175), otherwise
> +            "bootfile_name_alt" will be used.
> +        </column>
>        </group>
>
>        <group title="DHCP Options of type host_id">
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 27a94e64f..60066ebe2 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -1280,7 +1280,7 @@ reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
>      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
>  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot");
>      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot");
> -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
> +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
>  reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5},arp_cache_timeout=10,tcp_keepalive_interval=10);
>      formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5}, arp_cache_timeout = 10, tcp_keepalive_interval = 10);
>      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05.23.04.00.00.00.0a.26.04.00.00.00.0a,pause)
> @@ -1292,10 +1292,10 @@ reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,
>      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause)
>  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org");
>      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org");
> -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
> +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
>  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
>      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
> -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
> +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
>
>  reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
>      Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
> @@ -5310,10 +5310,10 @@ sleep 2
>  as hv1 ovs-vsctl show
>
>  # This shell function sends a DHCP request packet
> -# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ...
> +# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP ETH_BOOT USE_IP ...
>  test_dhcp() {
> -    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8
> -    shift; shift; shift; shift; shift; shift; shift; shift;
> +    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 eth_boot=$8 use_ip=$9
> +    shift; shift; shift; shift; shift; shift; shift; shift; shift;
>
>      if test $use_ip != 0; then
>          src_ip=$1
> @@ -5325,11 +5325,21 @@ test_dhcp() {
>      fi
>
>      if test $request_ip != 0; then
> -        ip_len=0120
> -        udp_len=010b
> +        if test $eth_boot != 0; then
> +            ip_len=0124
> +            udp_len=010f
> +        else
> +            ip_len=0120
> +            udp_len=010b
> +        fi
>      else
> -        ip_len=011a
> -        udp_len=0106
> +        if test $eth_boot != 0; then
> +            ip_len=011e
> +            udp_len=010a
> +        else
> +            ip_len=011a
> +            udp_len=0106
> +        fi
>      fi
>
>      if test $broadcast != 0; then
> @@ -5370,6 +5380,9 @@ test_dhcp() {
>          # dhcp requested ip
>          request=${request}3204${request_ip}
>      fi
> +    if test $eth_boot != 0; then
> +        request=${request}af020000
> +    fi
>      # dhcp end option
>      request=${request}ff
>
> @@ -5465,7 +5478,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=0
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> +test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 1.
>  OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5491,7 +5504,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=$offer_ip
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 2.
>  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5515,7 +5528,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=`ip_to_hex 10 0 0 7`
>  expected_dhcp_opts=""
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 3.
>  OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5539,7 +5552,7 @@ rm -f 2.expected
>  ciaddr=`ip_to_hex 0 0 0 0`
>  offer_ip=0
>  request_ip=0
> -test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1
> +test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 0 1 1
>
>  # NXT_RESUMEs should be 4.
>  OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5556,18 +5569,18 @@ rm -f 2.expected
>  # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
>
>  ciaddr=`ip_to_hex 0 0 0 0`
> -test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0
> +test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 0 4 0
>
>  # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
>  # this lport.
>  ciaddr=`ip_to_hex 0 0 0 0`
> -test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0
> +test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 0 3 0
>
>  # NXT_RESUMEs should be 4.
>  OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
>
> -#OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
> -#OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
> +OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
> +OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
>
>  # Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
>  # and ip4.dst set to 10.0.0.1.
> @@ -5578,7 +5591,7 @@ request_ip=0
>  src_ip=$offer_ip
>  dst_ip=$server_ip
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 5.
>  OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5604,7 +5617,7 @@ request_ip=0
>  src_ip=$offer_ip
>  dst_ip=`ip_to_hex 255 255 255 255`
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 6.
>  OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5630,7 +5643,7 @@ request_ip=0
>  src_ip=$offer_ip
>  dst_ip=`ip_to_hex 255 255 255 255`
>  expected_dhcp_opts=""
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 7.
>  OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5656,7 +5669,7 @@ request_ip=0
>  src_ip=$offer_ip
>  dst_ip=`ip_to_hex 255 255 255 255`
>  expected_dhcp_opts=""
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 8.
>  OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5678,7 +5691,7 @@ rm -f 2.expected
>  ciaddr=`ip_to_hex 0 0 0 0`
>  src_ip=`ip_to_hex 10 0 0 6`
>  dst_ip=`ip_to_hex 10 0 0 4`
> -test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1
> +test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 0 1 $src_ip $dst_ip 1
>
>  # NXT_RESUMEs should be 8.
>  OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5697,7 +5710,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=0
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> +test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 9.
>  OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5720,7 +5733,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 10 0 0 6`
>  request_ip=0
>  expected_dhcp_opts=0
> -test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001
> +test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001
>
>  # NXT_RESUMEs should be 10.
>  OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> @@ -5747,7 +5760,7 @@ dst_ip=$server_ip
>  # In the expected_dhcp_opts we should not see 330400000e10 which is
>  # dhcp lease time option and 0104ffffff00 which is subnet mask option.
>  expected_dhcp_opts=03040a00000136040a000001
> -test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 11.
>  OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> @@ -5777,7 +5790,7 @@ dst_ip=$server_ip
>  # In the expected_dhcp_opts we should not see 330400000e10 which is
>  # dhcp lease time option.
>  expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 12.
>  OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> @@ -5804,7 +5817,7 @@ dst_ip=$server_ip
>  # In the expected_dhcp_opts we should not see 330400000e10 which is
>  # dhcp lease time option and 0104ffffff00 which is subnet mask option.
>  expected_dhcp_opts=03040a00000136040a000001
> -test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 13.
>  OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> @@ -5835,7 +5848,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=$offer_ip
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 14.
>  OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5866,7 +5879,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=$offer_ip
>  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 15.
>  OVS_WAIT_UNTIL([test 15 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5897,7 +5910,7 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=$offer_ip
>  expected_dhcp_opts=771305746573743103636f6d00057465737432c006330400000e100104ffffff0003040a00000136040a000001
> -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
>
>  # NXT_RESUMEs should be 16.
>  OVS_WAIT_UNTIL([test 16 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> @@ -5915,9 +5928,42 @@ server_ip=`ip_to_hex 10 0 0 1`
>  ciaddr=`ip_to_hex 0 0 0 0`
>  request_ip=0
>  expected_dhcp_opts=""
> -test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> +test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
>  AT_CHECK([fgrep -iq 'DHCPDECLINE from f0:00:00:00:00:01, 10.0.0.4 duplicated' hv1/ovn-controller.log], [0], [])
>
> +# Send Etherboot.
> +
> +reset_pcap_file hv1-vif1 hv1/vif1
> +reset_pcap_file hv1-vif2 hv1/vif2
> +rm -f 1.expected
> +rm -f 2.expected
> +
> +ovn-nbctl --all destroy dhcp-option
> +
> +ovn-nbctl dhcp-options-create 10.0.0.0/24
> +d3=$(ovn-nbctl --bare --columns=_uuid find dhcp_options cidr="10.0.0.0/24")
> +ovn-nbctl dhcp-options-set-options $d3 \
> +   server_id=10.0.0.1 server_mac=ff:10:00:00:00:01 \
> +   lease_time=3600 router=10.0.0.1 bootfile_name_alt=\"bootfile_name_alt\" \
> +   bootfile_name=\"bootfile\"
> +
> +ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 $d3
> +
> +offer_ip=`ip_to_hex 10 0 0 4`
> +server_ip=`ip_to_hex 10 0 0 1`
> +ciaddr=`ip_to_hex 0 0 0 0`
> +request_ip=0
> +boofile=4308626f6f7466696c65
> +expected_dhcp_opts=${boofile}330400000e100104ffffff0003040a00000136040a000001
> +test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 1 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> +
> +$PYTHON "$ovs_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])
> +
>  OVN_CLEANUP([hv1])
>
>  AT_CLEANUP
> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> index d94ab025d..80d99b7a8 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -191,6 +191,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
>      dhcp_opt_add(dhcp_opts, "arp_cache_timeout", 35, "uint32");
>      dhcp_opt_add(dhcp_opts, "tcp_keepalive_interval", 38, "uint32");
>      dhcp_opt_add(dhcp_opts, "domain_search_list", 119, "domains");
> +    dhcp_opt_add(dhcp_opts, "bootfile_name_alt", 254, "str");
>
>      /* DHCPv6 options. */
>      hmap_init(dhcpv6_opts);
> --
> 2.26.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Numan Siddique Oct. 27, 2020, 2:19 p.m. UTC | #2
On Tue, Oct 27, 2020 at 7:40 PM Numan Siddique <numans@ovn.org> wrote:
>
> On Fri, Oct 23, 2020 at 4:04 PM Lorenzo Bianconi
> <lorenzo.bianconi@redhat.com> wrote:
> >
> > Add iPXE support to OVN introducing "bootfile_name_alt" dhcp option.
> > "bootfile_name_alt" dhcp userdata is encoded as option 254 since
> > it is not currently used.
> > When both "bootfile_name" and "bootfile_name_alt" are provided
> > by the CMS, "bootfile_name" will be used for option 67 if the
> > dhcp request contains etherboot option (175), otherwise
> > "bootfile_name_alt" will be used.
> > "bootfile_name" and "bootfile_name_alt" are placed just after offer_ip
> > in userdata buffer.
> >
> > Tested-by: Lucas Alvares Gomes Martins <lmartins@redhat.com>
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
>
> Thanks Lorenzo.
>
> I did the below changes and applied to master.
>
> **********
>
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 6f5499f0b..f15afc54f 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -2024,16 +2024,16 @@ pinctrl_handle_put_dhcp_opts(
>       */
>      struct dhcp_opt_header *in_dhcp_opt =
>          (struct dhcp_opt_header *)reply_dhcp_opts_ptr->data;
> -    if (in_dhcp_opt->code == 67) { /* DHCP_OPT_BOOTFILENAME */
> +    if (in_dhcp_opt->code == DHCP_OPT_BOOTFILE_CODE) {
>          unsigned char *ptr = (unsigned char *)in_dhcp_opt;
>          int len = sizeof *in_dhcp_opt + in_dhcp_opt->len;
>          struct dhcp_opt_header *next_dhcp_opt =
>              (struct dhcp_opt_header *)(ptr + len);
>
> -        if (next_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> +        if (next_dhcp_opt->code == DHCP_OPT_BOOTFILE_ALT_CODE) {
>              if (!ipxe_req) {
>                  ofpbuf_pull(reply_dhcp_opts_ptr, len);
> -                next_dhcp_opt->code = 67;
> +                next_dhcp_opt->code = DHCP_OPT_BOOTFILE_CODE;
>              } else {
>                  char *buf = xmalloc(len);
>
> @@ -2044,8 +2044,8 @@ pinctrl_handle_put_dhcp_opts(
>                  free(buf);
>              }
>          }
> -    } else if (in_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> -        in_dhcp_opt->code = 67;
> +    } else if (in_dhcp_opt->code == DHCP_OPT_BOOTFILE_ALT_CODE) {
> +        in_dhcp_opt->code = DHCP_OPT_BOOTFILE_CODE;
>      }
>
>      uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
> diff --git a/lib/actions.c b/lib/actions.c
> index b0d03bc96..23e54ef2a 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -2542,9 +2542,9 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
>      ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
>      ofpbuf_put(ofpacts, &offerip, sizeof offerip);
>
> -    /* Encode and bootfile_name opt (67) */
> -    const struct ovnact_gen_option *boot_opt = find_opt(
> -            pdo->options, pdo->n_options, 67);
> +    /* Encode bootfile_name opt (67) */
> +    const struct ovnact_gen_option *boot_opt =
> +        find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_CODE);
>      if (boot_opt) {
>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>          const union expr_constant *c = boot_opt->value.values;
> @@ -2553,8 +2553,8 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
>          ofpbuf_put(ofpacts, c->string, opt_header[1]);
>      }
>      /* Encode bootfile_name_alt opt (254) */
> -    const struct ovnact_gen_option *boot_alt_opt = find_opt(
> -            pdo->options, pdo->n_options, 254);
> +    const struct ovnact_gen_option *boot_alt_opt =
> +        find_opt(pdo->options, pdo->n_options, DHCP_OPT_BOOTFILE_ALT_CODE);
>      if (boot_alt_opt) {
>          uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
>          const union expr_constant *c = boot_alt_opt->value.values;
> @@ -2565,7 +2565,7 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
>
>      for (const struct ovnact_gen_option *o = pdo->options;
>           o < &pdo->options[pdo->n_options]; o++) {
> -        if (o != offerip_opt && o != boot_alt_opt && o != boot_opt) {
> +        if (o != offerip_opt && o != boot_opt && o != boot_alt_opt) {
>              encode_put_dhcpv4_option(o, ofpacts);
>          }
>      }
> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> index fb0088582..c3e8fd660 100644
> --- a/lib/ovn-l7.h
> +++ b/lib/ovn-l7.h
> @@ -93,10 +93,14 @@ struct gen_opts_map {
>  #define DHCP_OPT_DOMAIN_SEARCH_LIST \
>      DHCP_OPTION("domain_search_list", 119, "domains")
>
> -/* let's use unused 254 option for iPXE bootfile_name_alt
> - * userdata dhcp option
> +#define DHCP_OPT_BOOTFILE_CODE 67
> +
> +/* Use unused 254 option for iPXE bootfile_name_alt userdata DHCP option.
> + * This option code is replaced by 67 when sending the DHCP reply.
>   */
> -#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", 254, "str")
> +#define DHCP_OPT_BOOTFILE_ALT_CODE 254
> +#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", \
> +                                          DHCP_OPT_BOOTFILE_ALT_CODE, "str")
>
>  #define DHCP_OPT_ETHERBOOT     175
>
>
> *********
>
> > ---
> > Changes since v2:
> > - encode "bootfile_name" and "bootfile_name_alt" are placed just after
> >   offer_ip in userdata buffer in order to avoid loops in
> >   pinctrl_handle_put_dhcp_opts()
> > Changes since v1:
> > - encode bootfile_name_alt option as 254 in order to not increase userdata
> >   buffer size
> > ---
> >  controller/pinctrl.c |  30 ++++++++++++
> >  lib/actions.c        |  33 ++++++++++---
> >  lib/ovn-l7.h         |   7 +++
> >  northd/ovn-northd.c  |   1 +
> >  ovn-nb.xml           |  10 ++++
> >  tests/ovn.at         | 110 ++++++++++++++++++++++++++++++-------------
> >  tests/test-ovn.c     |   1 +
> >  7 files changed, 154 insertions(+), 38 deletions(-)
> >
> > diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> > index d85ba504d..6f5499f0b 100644
> > --- a/controller/pinctrl.c
> > +++ b/controller/pinctrl.c
> > @@ -1868,6 +1868,7 @@ pinctrl_handle_put_dhcp_opts(
> >      }
> >      in_dhcp_ptr += sizeof magic_cookie;
> >
> > +    bool ipxe_req = false;
> >      const uint8_t *in_dhcp_msg_type = NULL;
> >      ovs_be32 request_ip = in_dhcp_data->ciaddr;
> >      while (in_dhcp_ptr < end) {
> > @@ -1900,6 +1901,9 @@ pinctrl_handle_put_dhcp_opts(
> >                  request_ip = get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
> >              }
> >              break;
> > +        case DHCP_OPT_ETHERBOOT:
> > +            ipxe_req = true;
> > +            break;
> >          default:
> >              break;
> >          }
> > @@ -2018,6 +2022,32 @@ pinctrl_handle_put_dhcp_opts(
> >       *| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding|
> >       * --------------------------------------------------------------
> >       */
> > +    struct dhcp_opt_header *in_dhcp_opt =
> > +        (struct dhcp_opt_header *)reply_dhcp_opts_ptr->data;
> > +    if (in_dhcp_opt->code == 67) { /* DHCP_OPT_BOOTFILENAME */
> > +        unsigned char *ptr = (unsigned char *)in_dhcp_opt;
> > +        int len = sizeof *in_dhcp_opt + in_dhcp_opt->len;
> > +        struct dhcp_opt_header *next_dhcp_opt =
> > +            (struct dhcp_opt_header *)(ptr + len);
> > +
> > +        if (next_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> > +            if (!ipxe_req) {
> > +                ofpbuf_pull(reply_dhcp_opts_ptr, len);
> > +                next_dhcp_opt->code = 67;
> > +            } else {
> > +                char *buf = xmalloc(len);
> > +
> > +                memcpy(buf, in_dhcp_opt, len);
> > +                ofpbuf_pull(reply_dhcp_opts_ptr,
> > +                            sizeof *in_dhcp_opt + next_dhcp_opt->len);
> > +                memcpy(reply_dhcp_opts_ptr->data, buf, len);
> > +                free(buf);
> > +            }
> > +        }
> > +    } else if (in_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
> > +        in_dhcp_opt->code = 67;
> > +    }
> > +
> >      uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
> >      if (msg_type != DHCP_MSG_NAK) {
> >          new_l4_size += reply_dhcp_opts_ptr->size;
> > diff --git a/lib/actions.c b/lib/actions.c
> > index 1e1bdeff2..b0d03bc96 100644
> > --- a/lib/actions.c
> > +++ b/lib/actions.c
> > @@ -2086,10 +2086,10 @@ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
> >  }
> >
> >  static const struct ovnact_gen_option *
> > -find_offerip(const struct ovnact_gen_option *options, size_t n)
> > +find_opt(const struct ovnact_gen_option *options, size_t n, size_t code)
> >  {
> >      for (const struct ovnact_gen_option *o = options; o < &options[n]; o++) {
> > -        if (o->option->code == 0) {
> > +        if (o->option->code == code) {
> >              return o;
> >          }
> >      }
> > @@ -2288,7 +2288,7 @@ parse_put_dhcp_opts(struct action_context *ctx,
> >      parse_put_opts(ctx, dst, po, dhcp_opts, opts_type);
> >
> >      if (!ctx->lexer->error && po->ovnact.type == OVNACT_PUT_DHCPV4_OPTS
> > -        && !find_offerip(po->options, po->n_options)) {
> > +        && !find_opt(po->options, po->n_options, 0)) {
> >          lexer_error(ctx->lexer,
> >                      "put_dhcp_opts requires offerip to be specified.");
> >          return;
> > @@ -2537,14 +2537,35 @@ encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
> >      /* Encode the offerip option first, because it's a special case and needs
> >       * to be first in the actual DHCP response, and then encode the rest
> >       * (skipping offerip the second time around). */
> > -    const struct ovnact_gen_option *offerip_opt = find_offerip(
> > -        pdo->options, pdo->n_options);
> > +    const struct ovnact_gen_option *offerip_opt = find_opt(
> > +        pdo->options, pdo->n_options, 0);
> >      ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
> >      ofpbuf_put(ofpacts, &offerip, sizeof offerip);
> >
> > +    /* Encode and bootfile_name opt (67) */
> > +    const struct ovnact_gen_option *boot_opt = find_opt(
> > +            pdo->options, pdo->n_options, 67);
> > +    if (boot_opt) {
> > +        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > +        const union expr_constant *c = boot_opt->value.values;
> > +        opt_header[0] = boot_opt->option->code;
> > +        opt_header[1] = strlen(c->string);
> > +        ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > +    }
> > +    /* Encode bootfile_name_alt opt (254) */
> > +    const struct ovnact_gen_option *boot_alt_opt = find_opt(
> > +            pdo->options, pdo->n_options, 254);
> > +    if (boot_alt_opt) {
> > +        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
> > +        const union expr_constant *c = boot_alt_opt->value.values;
> > +        opt_header[0] = boot_alt_opt->option->code;
> > +        opt_header[1] = strlen(c->string);
> > +        ofpbuf_put(ofpacts, c->string, opt_header[1]);
> > +    }
> > +
> >      for (const struct ovnact_gen_option *o = pdo->options;
> >           o < &pdo->options[pdo->n_options]; o++) {
> > -        if (o != offerip_opt) {
> > +        if (o != offerip_opt && o != boot_alt_opt && o != boot_opt) {
> >              encode_put_dhcpv4_option(o, ofpacts);
> >          }
> >      }
> > diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> > index 30a795531..a2671ddd2 100644
> > --- a/lib/ovn-l7.h
> > +++ b/lib/ovn-l7.h
> > @@ -93,6 +93,13 @@ struct gen_opts_map {
> >  #define DHCP_OPT_DOMAIN_SEARCH_LIST \
> >      DHCP_OPTION("domain_search_list", 119, "domains")
> >
> > +/* let's use unused 254 option for iPXE bootfile_name_alt
> > + * userdata dhcp option
> > + */
> > +#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", 254, "str")
> > +
> > +#define DHCP_OPT_ETHERBOOT     175
> > +
> >  #define DHCP_OPT_ARP_CACHE_TIMEOUT \
> >      DHCP_OPTION("arp_cache_timeout", 35, "uint32")
> >  #define DHCP_OPT_TCP_KEEPALIVE_INTERVAL \
> > diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
> > index 5b76868df..a3cdf8afc 100644
> > --- a/northd/ovn-northd.c
> > +++ b/northd/ovn-northd.c
> > @@ -12472,6 +12472,7 @@ static struct gen_opts_map supported_dhcp_opts[] = {
> >      DHCP_OPT_ARP_CACHE_TIMEOUT,
> >      DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
> >      DHCP_OPT_DOMAIN_SEARCH_LIST,
> > +    DHCP_OPT_BOOTFILE_ALT,


Hi Ben,

This patch has one ovn-northd change - which adds a new row to SB DHCP_Options.

The test case in ovn.at  - "49: ovn -- dhcpv4 : 1 HV, 2 LS, 2 LSPs/LS"
would fail because of this.
Can you please add the corresponding code in northd/ovn_northd.dl for this ?

Since this patch would require a very small change in the ddlog code,
I went ahead and applied this patch
even though we decided not to apply any new feature patches in northd
until ddlog patches are in.
Hope it's fine with you.

Thanks
Numan

> >  };
> >
> >  static struct gen_opts_map supported_dhcpv6_opts[] = {
> > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > index 86195af34..43694535e 100644
> > --- a/ovn-nb.xml
> > +++ b/ovn-nb.xml
> > @@ -3096,6 +3096,16 @@
> >              resolving hostnames via the Domain Name System.
> >            </p>
> >          </column>
> > +
> > +        <column name="options" key="bootfile_name_alt">
> > +          <p>
> > +          </p>
> > +            "bootfile_name_alt" option is used to support iPXE.
> > +            When both "bootfile_name" and "bootfile_name_alt" are provided
> > +            by the CMS, "bootfile_name" will be used for option 67 if the
> > +            dhcp request contains etherboot option (175), otherwise
> > +            "bootfile_name_alt" will be used.
> > +        </column>
> >        </group>
> >
> >        <group title="DHCP Options of type host_id">
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index 27a94e64f..60066ebe2 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -1280,7 +1280,7 @@ reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
> >      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
> >  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot");
> >      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot");
> > -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
> > +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
> >  reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5},arp_cache_timeout=10,tcp_keepalive_interval=10);
> >      formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5}, arp_cache_timeout = 10, tcp_keepalive_interval = 10);
> >      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05.23.04.00.00.00.0a.26.04.00.00.00.0a,pause)
> > @@ -1292,10 +1292,10 @@ reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,
> >      encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause)
> >  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org");
> >      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org");
> > -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
> > +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
> >  reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
> >      formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
> > -    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
> > +    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
> >
> >  reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
> >      Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
> > @@ -5310,10 +5310,10 @@ sleep 2
> >  as hv1 ovs-vsctl show
> >
> >  # This shell function sends a DHCP request packet
> > -# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ...
> > +# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP ETH_BOOT USE_IP ...
> >  test_dhcp() {
> > -    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8
> > -    shift; shift; shift; shift; shift; shift; shift; shift;
> > +    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 eth_boot=$8 use_ip=$9
> > +    shift; shift; shift; shift; shift; shift; shift; shift; shift;
> >
> >      if test $use_ip != 0; then
> >          src_ip=$1
> > @@ -5325,11 +5325,21 @@ test_dhcp() {
> >      fi
> >
> >      if test $request_ip != 0; then
> > -        ip_len=0120
> > -        udp_len=010b
> > +        if test $eth_boot != 0; then
> > +            ip_len=0124
> > +            udp_len=010f
> > +        else
> > +            ip_len=0120
> > +            udp_len=010b
> > +        fi
> >      else
> > -        ip_len=011a
> > -        udp_len=0106
> > +        if test $eth_boot != 0; then
> > +            ip_len=011e
> > +            udp_len=010a
> > +        else
> > +            ip_len=011a
> > +            udp_len=0106
> > +        fi
> >      fi
> >
> >      if test $broadcast != 0; then
> > @@ -5370,6 +5380,9 @@ test_dhcp() {
> >          # dhcp requested ip
> >          request=${request}3204${request_ip}
> >      fi
> > +    if test $eth_boot != 0; then
> > +        request=${request}af020000
> > +    fi
> >      # dhcp end option
> >      request=${request}ff
> >
> > @@ -5465,7 +5478,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=0
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> > +test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 1.
> >  OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5491,7 +5504,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=$offer_ip
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 2.
> >  OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5515,7 +5528,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=`ip_to_hex 10 0 0 7`
> >  expected_dhcp_opts=""
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 3.
> >  OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5539,7 +5552,7 @@ rm -f 2.expected
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  offer_ip=0
> >  request_ip=0
> > -test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1
> > +test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 0 1 1
> >
> >  # NXT_RESUMEs should be 4.
> >  OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5556,18 +5569,18 @@ rm -f 2.expected
> >  # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
> >
> >  ciaddr=`ip_to_hex 0 0 0 0`
> > -test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0
> > +test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 0 4 0
> >
> >  # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
> >  # this lport.
> >  ciaddr=`ip_to_hex 0 0 0 0`
> > -test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0
> > +test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 0 3 0
> >
> >  # NXT_RESUMEs should be 4.
> >  OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> >
> > -#OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
> > -#OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
> > +OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
> > +OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
> >
> >  # Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
> >  # and ip4.dst set to 10.0.0.1.
> > @@ -5578,7 +5591,7 @@ request_ip=0
> >  src_ip=$offer_ip
> >  dst_ip=$server_ip
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 5.
> >  OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5604,7 +5617,7 @@ request_ip=0
> >  src_ip=$offer_ip
> >  dst_ip=`ip_to_hex 255 255 255 255`
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 6.
> >  OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5630,7 +5643,7 @@ request_ip=0
> >  src_ip=$offer_ip
> >  dst_ip=`ip_to_hex 255 255 255 255`
> >  expected_dhcp_opts=""
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 7.
> >  OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5656,7 +5669,7 @@ request_ip=0
> >  src_ip=$offer_ip
> >  dst_ip=`ip_to_hex 255 255 255 255`
> >  expected_dhcp_opts=""
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 8.
> >  OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5678,7 +5691,7 @@ rm -f 2.expected
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  src_ip=`ip_to_hex 10 0 0 6`
> >  dst_ip=`ip_to_hex 10 0 0 4`
> > -test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1
> > +test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 0 1 $src_ip $dst_ip 1
> >
> >  # NXT_RESUMEs should be 8.
> >  OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5697,7 +5710,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=0
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> > +test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 9.
> >  OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5720,7 +5733,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 10 0 0 6`
> >  request_ip=0
> >  expected_dhcp_opts=0
> > -test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001
> > +test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001
> >
> >  # NXT_RESUMEs should be 10.
> >  OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> > @@ -5747,7 +5760,7 @@ dst_ip=$server_ip
> >  # In the expected_dhcp_opts we should not see 330400000e10 which is
> >  # dhcp lease time option and 0104ffffff00 which is subnet mask option.
> >  expected_dhcp_opts=03040a00000136040a000001
> > -test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 11.
> >  OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> > @@ -5777,7 +5790,7 @@ dst_ip=$server_ip
> >  # In the expected_dhcp_opts we should not see 330400000e10 which is
> >  # dhcp lease time option.
> >  expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 12.
> >  OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> > @@ -5804,7 +5817,7 @@ dst_ip=$server_ip
> >  # In the expected_dhcp_opts we should not see 330400000e10 which is
> >  # dhcp lease time option and 0104ffffff00 which is subnet mask option.
> >  expected_dhcp_opts=03040a00000136040a000001
> > -test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 13.
> >  OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
> > @@ -5835,7 +5848,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=$offer_ip
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 14.
> >  OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5866,7 +5879,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=$offer_ip
> >  expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 15.
> >  OVS_WAIT_UNTIL([test 15 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5897,7 +5910,7 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=$offer_ip
> >  expected_dhcp_opts=771305746573743103636f6d00057465737432c006330400000e100104ffffff0003040a00000136040a000001
> > -test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> > +test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
> >
> >  # NXT_RESUMEs should be 16.
> >  OVS_WAIT_UNTIL([test 16 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
> > @@ -5915,9 +5928,42 @@ server_ip=`ip_to_hex 10 0 0 1`
> >  ciaddr=`ip_to_hex 0 0 0 0`
> >  request_ip=0
> >  expected_dhcp_opts=""
> > -test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> > +test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> >  AT_CHECK([fgrep -iq 'DHCPDECLINE from f0:00:00:00:00:01, 10.0.0.4 duplicated' hv1/ovn-controller.log], [0], [])
> >
> > +# Send Etherboot.
> > +
> > +reset_pcap_file hv1-vif1 hv1/vif1
> > +reset_pcap_file hv1-vif2 hv1/vif2
> > +rm -f 1.expected
> > +rm -f 2.expected
> > +
> > +ovn-nbctl --all destroy dhcp-option
> > +
> > +ovn-nbctl dhcp-options-create 10.0.0.0/24
> > +d3=$(ovn-nbctl --bare --columns=_uuid find dhcp_options cidr="10.0.0.0/24")
> > +ovn-nbctl dhcp-options-set-options $d3 \
> > +   server_id=10.0.0.1 server_mac=ff:10:00:00:00:01 \
> > +   lease_time=3600 router=10.0.0.1 bootfile_name_alt=\"bootfile_name_alt\" \
> > +   bootfile_name=\"bootfile\"
> > +
> > +ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 $d3
> > +
> > +offer_ip=`ip_to_hex 10 0 0 4`
> > +server_ip=`ip_to_hex 10 0 0 1`
> > +ciaddr=`ip_to_hex 0 0 0 0`
> > +request_ip=0
> > +boofile=4308626f6f7466696c65
> > +expected_dhcp_opts=${boofile}330400000e100104ffffff0003040a00000136040a000001
> > +test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 1 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
> > +
> > +$PYTHON "$ovs_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])
> > +
> >  OVN_CLEANUP([hv1])
> >
> >  AT_CLEANUP
> > diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> > index d94ab025d..80d99b7a8 100644
> > --- a/tests/test-ovn.c
> > +++ b/tests/test-ovn.c
> > @@ -191,6 +191,7 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
> >      dhcp_opt_add(dhcp_opts, "arp_cache_timeout", 35, "uint32");
> >      dhcp_opt_add(dhcp_opts, "tcp_keepalive_interval", 38, "uint32");
> >      dhcp_opt_add(dhcp_opts, "domain_search_list", 119, "domains");
> > +    dhcp_opt_add(dhcp_opts, "bootfile_name_alt", 254, "str");
> >
> >      /* DHCPv6 options. */
> >      hmap_init(dhcpv6_opts);
> > --
> > 2.26.2
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
diff mbox series

Patch

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index d85ba504d..6f5499f0b 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -1868,6 +1868,7 @@  pinctrl_handle_put_dhcp_opts(
     }
     in_dhcp_ptr += sizeof magic_cookie;
 
+    bool ipxe_req = false;
     const uint8_t *in_dhcp_msg_type = NULL;
     ovs_be32 request_ip = in_dhcp_data->ciaddr;
     while (in_dhcp_ptr < end) {
@@ -1900,6 +1901,9 @@  pinctrl_handle_put_dhcp_opts(
                 request_ip = get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
             }
             break;
+        case DHCP_OPT_ETHERBOOT:
+            ipxe_req = true;
+            break;
         default:
             break;
         }
@@ -2018,6 +2022,32 @@  pinctrl_handle_put_dhcp_opts(
      *| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding|
      * --------------------------------------------------------------
      */
+    struct dhcp_opt_header *in_dhcp_opt =
+        (struct dhcp_opt_header *)reply_dhcp_opts_ptr->data;
+    if (in_dhcp_opt->code == 67) { /* DHCP_OPT_BOOTFILENAME */
+        unsigned char *ptr = (unsigned char *)in_dhcp_opt;
+        int len = sizeof *in_dhcp_opt + in_dhcp_opt->len;
+        struct dhcp_opt_header *next_dhcp_opt =
+            (struct dhcp_opt_header *)(ptr + len);
+
+        if (next_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
+            if (!ipxe_req) {
+                ofpbuf_pull(reply_dhcp_opts_ptr, len);
+                next_dhcp_opt->code = 67;
+            } else {
+                char *buf = xmalloc(len);
+
+                memcpy(buf, in_dhcp_opt, len);
+                ofpbuf_pull(reply_dhcp_opts_ptr,
+                            sizeof *in_dhcp_opt + next_dhcp_opt->len);
+                memcpy(reply_dhcp_opts_ptr->data, buf, len);
+                free(buf);
+            }
+        }
+    } else if (in_dhcp_opt->code == 254) { /* DHCP_OPT_BOOTFILE_ALT */
+        in_dhcp_opt->code = 67;
+    }
+
     uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
     if (msg_type != DHCP_MSG_NAK) {
         new_l4_size += reply_dhcp_opts_ptr->size;
diff --git a/lib/actions.c b/lib/actions.c
index 1e1bdeff2..b0d03bc96 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2086,10 +2086,10 @@  parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
 }
 
 static const struct ovnact_gen_option *
-find_offerip(const struct ovnact_gen_option *options, size_t n)
+find_opt(const struct ovnact_gen_option *options, size_t n, size_t code)
 {
     for (const struct ovnact_gen_option *o = options; o < &options[n]; o++) {
-        if (o->option->code == 0) {
+        if (o->option->code == code) {
             return o;
         }
     }
@@ -2288,7 +2288,7 @@  parse_put_dhcp_opts(struct action_context *ctx,
     parse_put_opts(ctx, dst, po, dhcp_opts, opts_type);
 
     if (!ctx->lexer->error && po->ovnact.type == OVNACT_PUT_DHCPV4_OPTS
-        && !find_offerip(po->options, po->n_options)) {
+        && !find_opt(po->options, po->n_options, 0)) {
         lexer_error(ctx->lexer,
                     "put_dhcp_opts requires offerip to be specified.");
         return;
@@ -2537,14 +2537,35 @@  encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
     /* Encode the offerip option first, because it's a special case and needs
      * to be first in the actual DHCP response, and then encode the rest
      * (skipping offerip the second time around). */
-    const struct ovnact_gen_option *offerip_opt = find_offerip(
-        pdo->options, pdo->n_options);
+    const struct ovnact_gen_option *offerip_opt = find_opt(
+        pdo->options, pdo->n_options, 0);
     ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
     ofpbuf_put(ofpacts, &offerip, sizeof offerip);
 
+    /* Encode and bootfile_name opt (67) */
+    const struct ovnact_gen_option *boot_opt = find_opt(
+            pdo->options, pdo->n_options, 67);
+    if (boot_opt) {
+        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
+        const union expr_constant *c = boot_opt->value.values;
+        opt_header[0] = boot_opt->option->code;
+        opt_header[1] = strlen(c->string);
+        ofpbuf_put(ofpacts, c->string, opt_header[1]);
+    }
+    /* Encode bootfile_name_alt opt (254) */
+    const struct ovnact_gen_option *boot_alt_opt = find_opt(
+            pdo->options, pdo->n_options, 254);
+    if (boot_alt_opt) {
+        uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
+        const union expr_constant *c = boot_alt_opt->value.values;
+        opt_header[0] = boot_alt_opt->option->code;
+        opt_header[1] = strlen(c->string);
+        ofpbuf_put(ofpacts, c->string, opt_header[1]);
+    }
+
     for (const struct ovnact_gen_option *o = pdo->options;
          o < &pdo->options[pdo->n_options]; o++) {
-        if (o != offerip_opt) {
+        if (o != offerip_opt && o != boot_alt_opt && o != boot_opt) {
             encode_put_dhcpv4_option(o, ofpacts);
         }
     }
diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
index 30a795531..a2671ddd2 100644
--- a/lib/ovn-l7.h
+++ b/lib/ovn-l7.h
@@ -93,6 +93,13 @@  struct gen_opts_map {
 #define DHCP_OPT_DOMAIN_SEARCH_LIST \
     DHCP_OPTION("domain_search_list", 119, "domains")
 
+/* let's use unused 254 option for iPXE bootfile_name_alt
+ * userdata dhcp option
+ */
+#define DHCP_OPT_BOOTFILE_ALT DHCP_OPTION("bootfile_name_alt", 254, "str")
+
+#define DHCP_OPT_ETHERBOOT	175
+
 #define DHCP_OPT_ARP_CACHE_TIMEOUT \
     DHCP_OPTION("arp_cache_timeout", 35, "uint32")
 #define DHCP_OPT_TCP_KEEPALIVE_INTERVAL \
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 5b76868df..a3cdf8afc 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -12472,6 +12472,7 @@  static struct gen_opts_map supported_dhcp_opts[] = {
     DHCP_OPT_ARP_CACHE_TIMEOUT,
     DHCP_OPT_TCP_KEEPALIVE_INTERVAL,
     DHCP_OPT_DOMAIN_SEARCH_LIST,
+    DHCP_OPT_BOOTFILE_ALT,
 };
 
 static struct gen_opts_map supported_dhcpv6_opts[] = {
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 86195af34..43694535e 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3096,6 +3096,16 @@ 
             resolving hostnames via the Domain Name System.
           </p>
         </column>
+
+        <column name="options" key="bootfile_name_alt">
+          <p>
+          </p>
+            "bootfile_name_alt" option is used to support iPXE.
+            When both "bootfile_name" and "bootfile_name_alt" are provided
+            by the CMS, "bootfile_name" will be used for option 67 if the
+            dhcp request contains etherboot option (175), otherwise
+            "bootfile_name_alt" will be used.
+        </column>
       </group>
 
       <group title="DHCP Options of type host_id">
diff --git a/tests/ovn.at b/tests/ovn.at
index 27a94e64f..60066ebe2 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1280,7 +1280,7 @@  reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
 reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot");
     formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot");
-    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
 reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5},arp_cache_timeout=10,tcp_keepalive_interval=10);
     formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5}, arp_cache_timeout = 10, tcp_keepalive_interval = 10);
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05.23.04.00.00.00.0a.26.04.00.00.00.0a,pause)
@@ -1292,10 +1292,10 @@  reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,
     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.42.10.74.66.74.70.5f.73.65.72.76.65.72.5f.74.65.73.74,pause)
 reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org");
     formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org");
-    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.0f.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00,pause)
 reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot",domain_search_list="ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
     formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot", domain_search_list = "ovn.org,abc.ovn.org,def.ovn.org,ovn.test,def.ovn.test,test.org,abc.com");
-    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.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.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
+    encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.43.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.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.d2.09.2f.74.66.74.70.62.6f.6f.74.77.35.03.6f.76.6e.03.6f.72.67.00.03.61.62.63.c0.00.03.64.65.66.c0.00.03.6f.76.6e.04.74.65.73.74.00.03.64.65.66.c0.15.04.74.65.73.74.c0.04.03.61.62.63.03.63.6f.6d.00,pause)
 
 reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
     Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
@@ -5310,10 +5310,10 @@  sleep 2
 as hv1 ovs-vsctl show
 
 # This shell function sends a DHCP request packet
-# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ...
+# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP ETH_BOOT USE_IP ...
 test_dhcp() {
-    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8
-    shift; shift; shift; shift; shift; shift; shift; shift;
+    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 eth_boot=$8 use_ip=$9
+    shift; shift; shift; shift; shift; shift; shift; shift; shift;
 
     if test $use_ip != 0; then
         src_ip=$1
@@ -5325,11 +5325,21 @@  test_dhcp() {
     fi
 
     if test $request_ip != 0; then
-        ip_len=0120
-        udp_len=010b
+        if test $eth_boot != 0; then
+            ip_len=0124
+            udp_len=010f
+        else
+            ip_len=0120
+            udp_len=010b
+        fi
     else
-        ip_len=011a
-        udp_len=0106
+        if test $eth_boot != 0; then
+            ip_len=011e
+            udp_len=010a
+        else
+            ip_len=011a
+            udp_len=0106
+        fi
     fi
 
     if test $broadcast != 0; then
@@ -5370,6 +5380,9 @@  test_dhcp() {
         # dhcp requested ip
         request=${request}3204${request_ip}
     fi
+    if test $eth_boot != 0; then
+        request=${request}af020000
+    fi
     # dhcp end option
     request=${request}ff
 
@@ -5465,7 +5478,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=0
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
+test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 1.
 OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5491,7 +5504,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=$offer_ip
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 2.
 OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5515,7 +5528,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=`ip_to_hex 10 0 0 7`
 expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 3.
 OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5539,7 +5552,7 @@  rm -f 2.expected
 ciaddr=`ip_to_hex 0 0 0 0`
 offer_ip=0
 request_ip=0
-test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1
+test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 0 1 1
 
 # NXT_RESUMEs should be 4.
 OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5556,18 +5569,18 @@  rm -f 2.expected
 # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
 
 ciaddr=`ip_to_hex 0 0 0 0`
-test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0
+test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 0 4 0
 
 # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
 # this lport.
 ciaddr=`ip_to_hex 0 0 0 0`
-test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0
+test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 0 3 0
 
 # NXT_RESUMEs should be 4.
 OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
 
-#OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
-#OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
+OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
+OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
 
 # Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
 # and ip4.dst set to 10.0.0.1.
@@ -5578,7 +5591,7 @@  request_ip=0
 src_ip=$offer_ip
 dst_ip=$server_ip
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 5.
 OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5604,7 +5617,7 @@  request_ip=0
 src_ip=$offer_ip
 dst_ip=`ip_to_hex 255 255 255 255`
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 6.
 OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5630,7 +5643,7 @@  request_ip=0
 src_ip=$offer_ip
 dst_ip=`ip_to_hex 255 255 255 255`
 expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 7.
 OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5656,7 +5669,7 @@  request_ip=0
 src_ip=$offer_ip
 dst_ip=`ip_to_hex 255 255 255 255`
 expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 8.
 OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5678,7 +5691,7 @@  rm -f 2.expected
 ciaddr=`ip_to_hex 0 0 0 0`
 src_ip=`ip_to_hex 10 0 0 6`
 dst_ip=`ip_to_hex 10 0 0 4`
-test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1
+test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 0 1 $src_ip $dst_ip 1
 
 # NXT_RESUMEs should be 8.
 OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5697,7 +5710,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=0
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
+test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 9.
 OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5720,7 +5733,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 10 0 0 6`
 request_ip=0
 expected_dhcp_opts=0
-test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001
+test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001
 
 # NXT_RESUMEs should be 10.
 OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
@@ -5747,7 +5760,7 @@  dst_ip=$server_ip
 # In the expected_dhcp_opts we should not see 330400000e10 which is
 # dhcp lease time option and 0104ffffff00 which is subnet mask option.
 expected_dhcp_opts=03040a00000136040a000001
-test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 11.
 OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
@@ -5777,7 +5790,7 @@  dst_ip=$server_ip
 # In the expected_dhcp_opts we should not see 330400000e10 which is
 # dhcp lease time option.
 expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 12.
 OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
@@ -5804,7 +5817,7 @@  dst_ip=$server_ip
 # In the expected_dhcp_opts we should not see 330400000e10 which is
 # dhcp lease time option and 0104ffffff00 which is subnet mask option.
 expected_dhcp_opts=03040a00000136040a000001
-test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 13.
 OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
@@ -5835,7 +5848,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=$offer_ip
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a00000142040a0a0a0a
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 14.
 OVS_WAIT_UNTIL([test 14 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5866,7 +5879,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=$offer_ip
 expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a0000014210746573745f746674705f736572766572
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 15.
 OVS_WAIT_UNTIL([test 15 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5897,7 +5910,7 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=$offer_ip
 expected_dhcp_opts=771305746573743103636f6d00057465737432c006330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
+test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
 
 # NXT_RESUMEs should be 16.
 OVS_WAIT_UNTIL([test 16 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
@@ -5915,9 +5928,42 @@  server_ip=`ip_to_hex 10 0 0 1`
 ciaddr=`ip_to_hex 0 0 0 0`
 request_ip=0
 expected_dhcp_opts=""
-test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
+test_dhcp 1 f00000000001 04 0 $ciaddr $offer_ip $request_ip 0 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
 AT_CHECK([fgrep -iq 'DHCPDECLINE from f0:00:00:00:00:01, 10.0.0.4 duplicated' hv1/ovn-controller.log], [0], [])
 
+# Send Etherboot.
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+rm -f 1.expected
+rm -f 2.expected
+
+ovn-nbctl --all destroy dhcp-option
+
+ovn-nbctl dhcp-options-create 10.0.0.0/24
+d3=$(ovn-nbctl --bare --columns=_uuid find dhcp_options cidr="10.0.0.0/24")
+ovn-nbctl dhcp-options-set-options $d3 \
+   server_id=10.0.0.1 server_mac=ff:10:00:00:00:01 \
+   lease_time=3600 router=10.0.0.1 bootfile_name_alt=\"bootfile_name_alt\" \
+   bootfile_name=\"bootfile\"
+
+ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 $d3
+
+offer_ip=`ip_to_hex 10 0 0 4`
+server_ip=`ip_to_hex 10 0 0 1`
+ciaddr=`ip_to_hex 0 0 0 0`
+request_ip=0
+boofile=4308626f6f7466696c65
+expected_dhcp_opts=${boofile}330400000e100104ffffff0003040a00000136040a000001
+test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 1 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
+
+$PYTHON "$ovs_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])
+
 OVN_CLEANUP([hv1])
 
 AT_CLEANUP
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index d94ab025d..80d99b7a8 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -191,6 +191,7 @@  create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
     dhcp_opt_add(dhcp_opts, "arp_cache_timeout", 35, "uint32");
     dhcp_opt_add(dhcp_opts, "tcp_keepalive_interval", 38, "uint32");
     dhcp_opt_add(dhcp_opts, "domain_search_list", 119, "domains");
+    dhcp_opt_add(dhcp_opts, "bootfile_name_alt", 254, "str");
 
     /* DHCPv6 options. */
     hmap_init(dhcpv6_opts);