diff mbox series

[ovs-dev,v6,1/3] actions: DHCP Relay Agent support for overlay IPv4 subnets.

Message ID 20240424095607.129155-2-naveen.yerramneni@nutanix.com
State Accepted
Headers show
Series DHCP Relay Agent support for overlay subnets. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_ovn-kubernetes success github build: passed
ovsrobot/github-robot-_Build_and_Test fail github build: failed

Commit Message

Naveen Yerramneni April 24, 2024, 9:56 a.m. UTC
NEW OVN ACTIONS
---------------
  1. dhcp_relay_req_chk(<relay-ip>, <server-ip>)
       - This action executes on the source node on which the DHCP request originated.
       - This action relays the DHCP request coming from client to the server.
         Relay-ip is used to update GIADDR in the DHCP header.
  2. dhcp_relay_resp_chk(<relay-ip>, <server-ip>)
       - This action executes on the first node (RC node) which processes
         the DHCP response from the server.
       - This action updates  the destination MAC and destination IP so that the response
         can be forwarded to the appropriate node from which request was originated.
       - Relay-ip, server-ip are used to validate GIADDR and SERVER ID in the DHCP payload.

Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
---
 include/ovn/actions.h |  27 ++++++++++
 lib/actions.c         | 116 ++++++++++++++++++++++++++++++++++++++++++
 ovn-sb.xml            |  62 ++++++++++++++++++++++
 tests/ovn.at          |  34 +++++++++++++
 utilities/ovn-trace.c |  67 ++++++++++++++++++++++++
 5 files changed, 306 insertions(+)

Comments

Numan Siddique April 24, 2024, 11:19 p.m. UTC | #1
On Wed, Apr 24, 2024 at 5:56 AM Naveen Yerramneni <
naveen.yerramneni@nutanix.com> wrote:

> NEW OVN ACTIONS
> ---------------
>   1. dhcp_relay_req_chk(<relay-ip>, <server-ip>)
>        - This action executes on the source node on which the DHCP request
> originated.
>        - This action relays the DHCP request coming from client to the
> server.
>          Relay-ip is used to update GIADDR in the DHCP header.
>   2. dhcp_relay_resp_chk(<relay-ip>, <server-ip>)
>        - This action executes on the first node (RC node) which processes
>          the DHCP response from the server.
>        - This action updates  the destination MAC and destination IP so
> that the response
>          can be forwarded to the appropriate node from which request was
> originated.
>        - Relay-ip, server-ip are used to validate GIADDR and SERVER ID in
> the DHCP payload.
>
> Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com>
>

Thanks.

I applied this patch to main with the below changes.  Changes are mostly
cosmetic.

----------------------------------------------------------------------------
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index b30cb96b29..ae0864fdd7 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -396,7 +396,6 @@ struct ovnact_put_opts {
 /* OVNACT_DHCP_RELAY. */
 struct ovnact_dhcp_relay {
     struct ovnact ovnact;
-    int family;
     struct expr_field dst;      /* 1-bit destination field. */
     ovs_be32 relay_ipv4;
     ovs_be32 server_ipv4;
diff --git a/lib/actions.c b/lib/actions.c
index 940209a7b7..94751d0597 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2692,12 +2692,10 @@ parse_dhcp_relay_chk(struct action_context *ctx,

     /* Parse relay ip and server ip. */
     if (ctx->lexer->token.format == LEX_F_IPV4) {
-        dhcp_relay->family = AF_INET;
         dhcp_relay->relay_ipv4 = ctx->lexer->token.value.ipv4;
         lexer_get(ctx->lexer);
         lexer_match(ctx->lexer, LEX_T_COMMA);
         if (ctx->lexer->token.format == LEX_F_IPV4) {
-            dhcp_relay->family = AF_INET;
             dhcp_relay->server_ipv4 = ctx->lexer->token.value.ipv4;
             lexer_get(ctx->lexer);
         } else {
@@ -2706,7 +2704,7 @@ parse_dhcp_relay_chk(struct action_context *ctx,
         }
     } else {
         lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp relay "
-            "and server ips");
+                           "and server ips");
         return;
     }
     lexer_force_match(ctx->lexer, LEX_T_RPAREN);
@@ -2726,9 +2724,9 @@ encode_dhcpv4_relay_chk(const struct
ovnact_dhcp_relay *dhcp_relay,
     ovs_be32 ofs = htonl(dst.ofs);
     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
     ofpbuf_put(ofpacts, &dhcp_relay->relay_ipv4,
-        sizeof(dhcp_relay->relay_ipv4));
+               sizeof(dhcp_relay->relay_ipv4));
     ofpbuf_put(ofpacts, &dhcp_relay->server_ipv4,
-        sizeof(dhcp_relay->server_ipv4));
+               sizeof(dhcp_relay->server_ipv4));
     encode_finish_controller_op(oc_offset, ofpacts);
 }

@@ -2736,7 +2734,7 @@ static void
 format_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
                             struct ds *s)
 {
-    format_dhcpv4_relay_chk("dhcp_relay_req_chk",dhcp_relay, s);
+    format_dhcpv4_relay_chk("dhcp_relay_req_chk", dhcp_relay, s);
 }

 static void
@@ -2752,7 +2750,7 @@ static void
 format_DHCPV4_RELAY_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
                              struct ds *s)
 {
-    format_dhcpv4_relay_chk("dhcp_relay_resp_chk",dhcp_relay, s);
+    format_dhcpv4_relay_chk("dhcp_relay_resp_chk", dhcp_relay, s);
 }

 static void
@@ -2764,8 +2762,7 @@ encode_DHCPV4_RELAY_RESP_CHK(const struct
ovnact_dhcp_relay *dhcp_relay,
                             ACTION_OPCODE_DHCP_RELAY_RESP_CHK);
 }

-static void ovnact_dhcp_relay_free(
-          struct ovnact_dhcp_relay *dhcp_relay OVS_UNUSED)
+static void ovnact_dhcp_relay_free(struct ovnact_dhcp_relay *dr OVS_UNUSED)
 {
 }

@@ -5472,11 +5469,11 @@ parse_set_action(struct action_context *ctx)
             parse_chk_lb_aff(ctx, &lhs,
                     ovnact_put_CHK_LB_AFF(ctx->ovnacts));
         } else if (lexer_match_id(ctx->lexer, "dhcp_relay_req_chk")) {
-            parse_dhcp_relay_chk(ctx, &lhs,
-                ovnact_put_DHCPV4_RELAY_REQ_CHK(ctx->ovnacts));
+            parse_dhcp_relay_chk(
+                ctx, &lhs, ovnact_put_DHCPV4_RELAY_REQ_CHK(ctx->ovnacts));
         } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_chk")) {
-            parse_dhcp_relay_chk(ctx, &lhs,
-                ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
+            parse_dhcp_relay_chk(
+                ctx, &lhs, ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
         } else {
             parse_assignment_action(ctx, false, &lhs);
         }
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index 49b2ab9545..13a4ea490a 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -2360,9 +2360,9 @@ execute_dhcpv4_relay_resp_chk(const struct
ovnact_dhcp_relay *dr,
     ovntrace_node_append(
         super, OVNTRACE_NODE_ERROR,
         "/* We assume that this packet is DHCPOFFER or DHCPACK and "
-            "DHCP broadcast flag is set. Dest IP is set to broadcast. "
-            "Dest MAC is set to broadcast but in real network this is
unicast "
-            "which is extracted from DHCP header. */");
+        "DHCP broadcast flag is set. Dest IP is set to broadcast. "
+        "Dest MAC is set to broadcast but in real network this is unicast "
+        "which is extracted from DHCP header. */");

     /* Assume DHCP broadcast flag is set */
     uflow->nw_dst = htonl(0xFFFFFFFF);
----------------------------------------------------------------------------

Numan

---
>  include/ovn/actions.h |  27 ++++++++++
>  lib/actions.c         | 116 ++++++++++++++++++++++++++++++++++++++++++
>  ovn-sb.xml            |  62 ++++++++++++++++++++++
>  tests/ovn.at          |  34 +++++++++++++
>  utilities/ovn-trace.c |  67 ++++++++++++++++++++++++
>  5 files changed, 306 insertions(+)
>
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> index f697dff39..ab2f3856c 100644
> --- a/include/ovn/actions.h
> +++ b/include/ovn/actions.h
> @@ -96,6 +96,8 @@ struct collector_set_ids;
>      OVNACT(LOOKUP_ND_IP,      ovnact_lookup_mac_bind_ip) \
>      OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
>      OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
> +    OVNACT(DHCPV4_RELAY_REQ_CHK,  ovnact_dhcp_relay)      \
> +    OVNACT(DHCPV4_RELAY_RESP_CHK, ovnact_dhcp_relay)      \
>      OVNACT(SET_QUEUE,         ovnact_set_queue)       \
>      OVNACT(DNS_LOOKUP,        ovnact_result)          \
>      OVNACT(LOG,               ovnact_log)             \
> @@ -389,6 +391,15 @@ struct ovnact_put_opts {
>      size_t n_options;
>  };
>
> +/* OVNACT_DHCP_RELAY. */
> +struct ovnact_dhcp_relay {
> +    struct ovnact ovnact;
> +    int family;
> +    struct expr_field dst;      /* 1-bit destination field. */
> +    ovs_be32 relay_ipv4;
> +    ovs_be32 server_ipv4;
> +};
> +
>  /* Valid arguments to SET_QUEUE action.
>   *
>   * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues should
> @@ -765,6 +776,22 @@ enum action_opcode {
>
>      /* multicast group split buffer action. */
>      ACTION_OPCODE_MG_SPLIT_BUF,
> +
> +    /* "dhcp_relay_req_chk(relay_ip, server_ip)".
> +     *
> +     * Arguments follow the action_header, in this format:
> +     *   - The 32-bit DHCP relay IP.
> +     *   - The 32-bit DHCP server IP.
> +     */
> +    ACTION_OPCODE_DHCP_RELAY_REQ_CHK,
> +
> +    /* "dhcp_relay_resp_chk(relay_ip, server_ip)".
> +     *
> +     * Arguments follow the action_header, in this format:
> +     *   - The 32-bit DHCP relay IP.
> +     *   - The 32-bit DHCP server IP.
> +     */
> +    ACTION_OPCODE_DHCP_RELAY_RESP_CHK,
>  };
>
>  /* Header. */
> diff --git a/lib/actions.c b/lib/actions.c
> index 361d55009..6cd60366a 100644
> --- a/lib/actions.c
> +++ b/lib/actions.c
> @@ -1869,6 +1869,8 @@ is_paused_nested_action(enum action_opcode opcode)
>      case ACTION_OPCODE_BFD_MSG:
>      case ACTION_OPCODE_ACTIVATION_STRATEGY_RARP:
>      case ACTION_OPCODE_MG_SPLIT_BUF:
> +    case ACTION_OPCODE_DHCP_RELAY_REQ_CHK:
> +    case ACTION_OPCODE_DHCP_RELAY_RESP_CHK:
>      default:
>          return false;
>      }
> @@ -2610,6 +2612,114 @@ ovnact_controller_event_free(struct
> ovnact_controller_event *event)
>      free_gen_options(event->options, event->n_options);
>  }
>
> +static void
> +format_dhcpv4_relay_chk(const char *name,
> +                        const struct ovnact_dhcp_relay *dhcp_relay,
> +                        struct ds *s)
> +{
> +    expr_field_format(&dhcp_relay->dst, s);
> +    ds_put_format(s, " = %s("IP_FMT", "IP_FMT");",
> +                  name,
> +                  IP_ARGS(dhcp_relay->relay_ipv4),
> +                  IP_ARGS(dhcp_relay->server_ipv4));
> +}
> +
> +static void
> +parse_dhcp_relay_chk(struct action_context *ctx,
> +                     const struct expr_field *dst,
> +                     struct ovnact_dhcp_relay *dhcp_relay)
> +{
> +    /* Skip dhcp_relay_req_chk/dhcp_relay_resp_chk( */
> +    lexer_force_match(ctx->lexer, LEX_T_LPAREN);
> +
> +    /* Validate that the destination is a 1-bit, modifiable field. */
> +    char *error = expr_type_check(dst, 1, true, ctx->scope);
> +    if (error) {
> +        lexer_error(ctx->lexer, "%s", error);
> +        free(error);
> +        return;
> +    }
> +    dhcp_relay->dst = *dst;
> +
> +    /* Parse relay ip and server ip. */
> +    if (ctx->lexer->token.format == LEX_F_IPV4) {
> +        dhcp_relay->family = AF_INET;
> +        dhcp_relay->relay_ipv4 = ctx->lexer->token.value.ipv4;
> +        lexer_get(ctx->lexer);
> +        lexer_match(ctx->lexer, LEX_T_COMMA);
> +        if (ctx->lexer->token.format == LEX_F_IPV4) {
> +            dhcp_relay->family = AF_INET;
> +            dhcp_relay->server_ipv4 = ctx->lexer->token.value.ipv4;
> +            lexer_get(ctx->lexer);
> +        } else {
> +            lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp server
> ip");
> +            return;
> +        }
> +    } else {
> +        lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp relay "
> +            "and server ips");
> +        return;
> +    }
> +    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> +}
> +
> +static void
> +encode_dhcpv4_relay_chk(const struct ovnact_dhcp_relay *dhcp_relay,
> +                        const struct ovnact_encode_params *ep,
> +                        struct ofpbuf *ofpacts,
> +                        enum action_opcode dhcp_relay_opcode)
> +{
> +    struct mf_subfield dst = expr_resolve_field(&dhcp_relay->dst);
> +    size_t oc_offset = encode_start_controller_op(dhcp_relay_opcode, true,
> +                                                  ep->ctrl_meter_id,
> +                                                  ofpacts);
> +    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
> +    ovs_be32 ofs = htonl(dst.ofs);
> +    ofpbuf_put(ofpacts, &ofs, sizeof ofs);
> +    ofpbuf_put(ofpacts, &dhcp_relay->relay_ipv4,
> +        sizeof(dhcp_relay->relay_ipv4));
> +    ofpbuf_put(ofpacts, &dhcp_relay->server_ipv4,
> +        sizeof(dhcp_relay->server_ipv4));
> +    encode_finish_controller_op(oc_offset, ofpacts);
> +}
> +
> +static void
> +format_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
> +                            struct ds *s)
> +{
> +    format_dhcpv4_relay_chk("dhcp_relay_req_chk",dhcp_relay, s);
> +}
> +
> +static void
> +encode_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
> +                            const struct ovnact_encode_params *ep,
> +                            struct ofpbuf *ofpacts)
> +{
> +    encode_dhcpv4_relay_chk(dhcp_relay, ep, ofpacts,
> +                            ACTION_OPCODE_DHCP_RELAY_REQ_CHK);
> +}
> +
> +static void
> +format_DHCPV4_RELAY_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
> +                             struct ds *s)
> +{
> +    format_dhcpv4_relay_chk("dhcp_relay_resp_chk",dhcp_relay, s);
> +}
> +
> +static void
> +encode_DHCPV4_RELAY_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
> +                             const struct ovnact_encode_params *ep,
> +                             struct ofpbuf *ofpacts)
> +{
> +    encode_dhcpv4_relay_chk(dhcp_relay, ep, ofpacts,
> +                            ACTION_OPCODE_DHCP_RELAY_RESP_CHK);
> +}
> +
> +static void ovnact_dhcp_relay_free(
> +          struct ovnact_dhcp_relay *dhcp_relay OVS_UNUSED)
> +{
> +}
> +
>  static void
>  parse_put_opts(struct action_context *ctx, const struct expr_field *dst,
>                 struct ovnact_put_opts *po, const struct hmap *gen_opts,
> @@ -5312,6 +5422,12 @@ parse_set_action(struct action_context *ctx)
>                     lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
>              parse_chk_lb_aff(ctx, &lhs,
>                      ovnact_put_CHK_LB_AFF(ctx->ovnacts));
> +        } else if (lexer_match_id(ctx->lexer, "dhcp_relay_req_chk")) {
> +            parse_dhcp_relay_chk(ctx, &lhs,
> +                ovnact_put_DHCPV4_RELAY_REQ_CHK(ctx->ovnacts));
> +        } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_chk")) {
> +            parse_dhcp_relay_chk(ctx, &lhs,
> +                ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
>          } else {
>              parse_assignment_action(ctx, false, &lhs);
>          }
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index 4c26c6714..81ed824d0 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -2801,6 +2801,68 @@ tcp.flags = RST;
>              use statistics of MAC cache.
>            </p>
>          </dd>
> +
> +        <dt><code><var>R</var> = dhcp_relay_req_chk(<var>relay-ip</var>,
> +                                      <var>server-ip</var>);</code></dt>
> +        <dd>
> +          <p>
> +            <b>Parameters</b>: Logical Router Port IP
> <var>relay-ip</var>, DHCP
> +            Server IP <var>server-ip</var>.
> +          </p>
> +
> +          <p>
> +            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
> +          </p>
> +
> +          <p>
> +            This action executes on the source node on which the DHCP
> request
> +            (DHCPDISCOVER or DHCPREQUEST) originated.
> +          </p>
> +
> +          <p>
> +            When this action applied successfully on the DHCP request
> packet,
> +            it updates GIADDR in the DHCP packet with <var>relay-ip</var>
> and
> +            stores 1 in R.
> +          </p>
> +
> +          <p>
> +            When this action failed to apply on the packet, it leaves the
> +            packet unchanged and stores 0 in R.
> +          </p>
> +        </dd>
> +
> +        <dt><code><var>R</var> = dhcp_relay_resp_chk(<var>relay-ip</var>,
> +                                      <var>server-ip</var>);</code></dt>
> +        <dd>
> +          <p>
> +            <b>Parameters</b>: Logical Router Port IP
> <var>relay-ip</var>, DHCP
> +            Server IP <var>server-ip</var>.
> +          </p>
> +
> +          <p>
> +            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
> +          </p>
> +
> +          <p>
> +            This action executes on the first node (Redirect Chassis node)
> +            which processes the DHCP response(DHCPOFFER, DHCPACK) from
> the DHCP
> +            server.
> +          </p>
> +
> +          <p>
> +            When this action applied successfully on the DHCP response
> packet,
> +            it updates the destination MAC and destination IP in the
> packet and
> +            stores 1 in R.
> +            <var>relay-ip</var> and <var>server-ip</var> are used to
> validate
> +            GIADDR and SERVER-ID in the DHCP response packet.
> +          </p>
> +
> +          <p>
> +            When this action failed to apply on the packet, it leaves the
> +            packet unchanged and stores 0 in R.
> +          </p>
> +        </dd>
> +
>        </dl>
>      </column>
>
> diff --git a/tests/ovn.at b/tests/ovn.at
> index dc6aafd53..1ad4159cf 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -1653,6 +1653,40 @@ reg1[[0]] = put_dhcp_opts(offerip=1.2.3.4,
> domain_name=1.2.3.4);
>  reg1[[0]] = put_dhcp_opts(offerip=1.2.3.4, domain_search_list=1.2.3.4);
>      DHCPv4 option domain_search_list requires string value.
>
> +#dhcp_relay_req_chk
> +reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
> +    encodes as
> controller(userdata=00.00.00.1c.00.00.00.00.80.01.08.08.00.00.00.07.c0.a8.01.01.ac.10.01.01,pause)
> +
> +reg9[[7]] = dhcp_relay_req_chk(192.168.1.1,172.16.1.1);
> +    formats as reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
> +    encodes as
> controller(userdata=00.00.00.1c.00.00.00.00.80.01.08.08.00.00.00.07.c0.a8.01.01.ac.10.01.01,pause)
> +
> +reg9[[7..8]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
> +    Cannot use 2-bit field reg9[[7..8]] where 1-bit field is required.
> +
> +reg9[[7]] = dhcp_relay_req_chk("192.168.1.1", "172.16.1.1");
> +    Syntax error at `"192.168.1.1"' expecting IPv4 dhcp relay and server
> ips.
> +
> +reg9[[7]] = dhcp_relay_req_chk(192.168.1, 172.16.1.1);
> +    Invalid numeric constant.
> +
> +#dhcp_relay_resp_chk
> +reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
> +    encodes as
> controller(userdata=00.00.00.1d.00.00.00.00.80.01.08.08.00.00.00.08.c0.a8.01.01.ac.10.01.01,pause)
> +
> +reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1,172.16.1.1);
> +    formats as reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
> +    encodes as
> controller(userdata=00.00.00.1d.00.00.00.00.80.01.08.08.00.00.00.08.c0.a8.01.01.ac.10.01.01,pause)
> +
> +reg9[[7..8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
> +    Cannot use 2-bit field reg9[[7..8]] where 1-bit field is required.
> +
> +reg9[[8]] = dhcp_relay_resp_chk("192.168.1.1", "172.16.1.1");
> +    Syntax error at `"192.168.1.1"' expecting IPv4 dhcp relay and server
> ips.
> +
> +reg9[[8]] = dhcp_relay_resp_chk(192.168.1, 172.16.1.1);
> +    Invalid numeric constant.
> +
>  # nd_ns
>  nd_ns { nd.target = xxreg0; output; };
>      encodes as controller(userdata=00.00.00.09.00.00.00.00.00.1c.00.18.00.
> 80.00.00.00.00.00.00.00.01.de
> .10.80.00.3e.10.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause)
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index ee086a7ae..0103253e1 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -2329,6 +2329,63 @@ execute_put_dhcp_opts(const struct ovnact_put_opts
> *pdo,
>      execute_put_opts(pdo, name, uflow, super);
>  }
>
> +static void
> +execute_dhcpv4_relay_req_chk(const struct ovnact_dhcp_relay *dr,
> +                             struct flow *uflow, struct ovs_list *super)
> +{
> +    ovntrace_node_append(
> +        super, OVNTRACE_NODE_ERROR,
> +        "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST.
> */");
> +
> +    struct ds s = DS_EMPTY_INITIALIZER;
> +    struct mf_subfield dst = expr_resolve_field(&dr->dst);
> +    if (!mf_is_register(dst.field->id)) {
> +        /* Format assignment. */
> +        ds_clear(&s);
> +        expr_field_format(&dr->dst, &s);
> +        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
> +                             "%s = 1", ds_cstr(&s));
> +    }
> +    ds_destroy(&s);
> +
> +    struct mf_subfield sf = expr_resolve_field(&dr->dst);
> +    union mf_subvalue sv = { .u8_val = 1 };
> +    mf_write_subfield_flow(&sf, &sv, uflow);
> +}
> +
> +static void
> +execute_dhcpv4_relay_resp_chk(const struct ovnact_dhcp_relay *dr,
> +                              struct flow *uflow, struct ovs_list *super)
> +{
> +    ovntrace_node_append(
> +        super, OVNTRACE_NODE_ERROR,
> +        "/* We assume that this packet is DHCPOFFER or DHCPACK and "
> +            "DHCP broadcast flag is set. Dest IP is set to broadcast. "
> +            "Dest MAC is set to broadcast but in real network this is
> unicast "
> +            "which is extracted from DHCP header. */");
> +
> +    /* Assume DHCP broadcast flag is set */
> +    uflow->nw_dst = htonl(0xFFFFFFFF);
> +    /* Dest MAC is set to broadcast but in real network this is unicast */
> +    struct eth_addr bcast_mac = { .ea = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> 0xFF}};
> +    uflow->dl_dst = bcast_mac;
> +
> +    struct ds s = DS_EMPTY_INITIALIZER;
> +    struct mf_subfield dst = expr_resolve_field(&dr->dst);
> +    if (!mf_is_register(dst.field->id)) {
> +        /* Format assignment. */
> +        ds_clear(&s);
> +        expr_field_format(&dr->dst, &s);
> +        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
> +                             "%s = 1", ds_cstr(&s));
> +    }
> +    ds_destroy(&s);
> +
> +    struct mf_subfield sf = expr_resolve_field(&dr->dst);
> +    union mf_subvalue sv = { .u8_val = 1 };
> +    mf_write_subfield_flow(&sf, &sv, uflow);
> +}
> +
>  static void
>  execute_put_nd_ra_opts(const struct ovnact_put_opts *pdo,
>                         const char *name, struct flow *uflow,
> @@ -3215,6 +3272,16 @@ trace_actions(const struct ovnact *ovnacts, size_t
> ovnacts_len,
>                                    "put_dhcpv6_opts", uflow, super);
>              break;
>
> +        case OVNACT_DHCPV4_RELAY_REQ_CHK:
> +
> execute_dhcpv4_relay_req_chk(ovnact_get_DHCPV4_RELAY_REQ_CHK(a),
> +                                         uflow, super);
> +            break;
> +
> +        case OVNACT_DHCPV4_RELAY_RESP_CHK:
> +
> execute_dhcpv4_relay_resp_chk(ovnact_get_DHCPV4_RELAY_RESP_CHK(a),
> +                                          uflow, super);
> +            break;
> +
>          case OVNACT_PUT_ND_RA_OPTS:
>              execute_put_nd_ra_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
>                                     "put_nd_ra_opts", uflow, super);
> --
> 2.36.6
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
diff mbox series

Patch

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index f697dff39..ab2f3856c 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -96,6 +96,8 @@  struct collector_set_ids;
     OVNACT(LOOKUP_ND_IP,      ovnact_lookup_mac_bind_ip) \
     OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
     OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
+    OVNACT(DHCPV4_RELAY_REQ_CHK,  ovnact_dhcp_relay)      \
+    OVNACT(DHCPV4_RELAY_RESP_CHK, ovnact_dhcp_relay)      \
     OVNACT(SET_QUEUE,         ovnact_set_queue)       \
     OVNACT(DNS_LOOKUP,        ovnact_result)          \
     OVNACT(LOG,               ovnact_log)             \
@@ -389,6 +391,15 @@  struct ovnact_put_opts {
     size_t n_options;
 };
 
+/* OVNACT_DHCP_RELAY. */
+struct ovnact_dhcp_relay {
+    struct ovnact ovnact;
+    int family;
+    struct expr_field dst;      /* 1-bit destination field. */
+    ovs_be32 relay_ipv4;
+    ovs_be32 server_ipv4;
+};
+
 /* Valid arguments to SET_QUEUE action.
  *
  * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues should
@@ -765,6 +776,22 @@  enum action_opcode {
 
     /* multicast group split buffer action. */
     ACTION_OPCODE_MG_SPLIT_BUF,
+
+    /* "dhcp_relay_req_chk(relay_ip, server_ip)".
+     *
+     * Arguments follow the action_header, in this format:
+     *   - The 32-bit DHCP relay IP.
+     *   - The 32-bit DHCP server IP.
+     */
+    ACTION_OPCODE_DHCP_RELAY_REQ_CHK,
+
+    /* "dhcp_relay_resp_chk(relay_ip, server_ip)".
+     *
+     * Arguments follow the action_header, in this format:
+     *   - The 32-bit DHCP relay IP.
+     *   - The 32-bit DHCP server IP.
+     */
+    ACTION_OPCODE_DHCP_RELAY_RESP_CHK,
 };
 
 /* Header. */
diff --git a/lib/actions.c b/lib/actions.c
index 361d55009..6cd60366a 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1869,6 +1869,8 @@  is_paused_nested_action(enum action_opcode opcode)
     case ACTION_OPCODE_BFD_MSG:
     case ACTION_OPCODE_ACTIVATION_STRATEGY_RARP:
     case ACTION_OPCODE_MG_SPLIT_BUF:
+    case ACTION_OPCODE_DHCP_RELAY_REQ_CHK:
+    case ACTION_OPCODE_DHCP_RELAY_RESP_CHK:
     default:
         return false;
     }
@@ -2610,6 +2612,114 @@  ovnact_controller_event_free(struct ovnact_controller_event *event)
     free_gen_options(event->options, event->n_options);
 }
 
+static void
+format_dhcpv4_relay_chk(const char *name,
+                        const struct ovnact_dhcp_relay *dhcp_relay,
+                        struct ds *s)
+{
+    expr_field_format(&dhcp_relay->dst, s);
+    ds_put_format(s, " = %s("IP_FMT", "IP_FMT");",
+                  name,
+                  IP_ARGS(dhcp_relay->relay_ipv4),
+                  IP_ARGS(dhcp_relay->server_ipv4));
+}
+
+static void
+parse_dhcp_relay_chk(struct action_context *ctx,
+                     const struct expr_field *dst,
+                     struct ovnact_dhcp_relay *dhcp_relay)
+{
+    /* Skip dhcp_relay_req_chk/dhcp_relay_resp_chk( */
+    lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+
+    /* Validate that the destination is a 1-bit, modifiable field. */
+    char *error = expr_type_check(dst, 1, true, ctx->scope);
+    if (error) {
+        lexer_error(ctx->lexer, "%s", error);
+        free(error);
+        return;
+    }
+    dhcp_relay->dst = *dst;
+
+    /* Parse relay ip and server ip. */
+    if (ctx->lexer->token.format == LEX_F_IPV4) {
+        dhcp_relay->family = AF_INET;
+        dhcp_relay->relay_ipv4 = ctx->lexer->token.value.ipv4;
+        lexer_get(ctx->lexer);
+        lexer_match(ctx->lexer, LEX_T_COMMA);
+        if (ctx->lexer->token.format == LEX_F_IPV4) {
+            dhcp_relay->family = AF_INET;
+            dhcp_relay->server_ipv4 = ctx->lexer->token.value.ipv4;
+            lexer_get(ctx->lexer);
+        } else {
+            lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp server ip");
+            return;
+        }
+    } else {
+        lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp relay "
+            "and server ips");
+        return;
+    }
+    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+encode_dhcpv4_relay_chk(const struct ovnact_dhcp_relay *dhcp_relay,
+                        const struct ovnact_encode_params *ep,
+                        struct ofpbuf *ofpacts,
+                        enum action_opcode dhcp_relay_opcode)
+{
+    struct mf_subfield dst = expr_resolve_field(&dhcp_relay->dst);
+    size_t oc_offset = encode_start_controller_op(dhcp_relay_opcode, true,
+                                                  ep->ctrl_meter_id,
+                                                  ofpacts);
+    nx_put_header(ofpacts, dst.field->id, OFP15_VERSION, false);
+    ovs_be32 ofs = htonl(dst.ofs);
+    ofpbuf_put(ofpacts, &ofs, sizeof ofs);
+    ofpbuf_put(ofpacts, &dhcp_relay->relay_ipv4,
+        sizeof(dhcp_relay->relay_ipv4));
+    ofpbuf_put(ofpacts, &dhcp_relay->server_ipv4,
+        sizeof(dhcp_relay->server_ipv4));
+    encode_finish_controller_op(oc_offset, ofpacts);
+}
+
+static void
+format_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                            struct ds *s)
+{
+    format_dhcpv4_relay_chk("dhcp_relay_req_chk",dhcp_relay, s);
+}
+
+static void
+encode_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                            const struct ovnact_encode_params *ep,
+                            struct ofpbuf *ofpacts)
+{
+    encode_dhcpv4_relay_chk(dhcp_relay, ep, ofpacts,
+                            ACTION_OPCODE_DHCP_RELAY_REQ_CHK);
+}
+
+static void
+format_DHCPV4_RELAY_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                             struct ds *s)
+{
+    format_dhcpv4_relay_chk("dhcp_relay_resp_chk",dhcp_relay, s);
+}
+
+static void
+encode_DHCPV4_RELAY_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                             const struct ovnact_encode_params *ep,
+                             struct ofpbuf *ofpacts)
+{
+    encode_dhcpv4_relay_chk(dhcp_relay, ep, ofpacts,
+                            ACTION_OPCODE_DHCP_RELAY_RESP_CHK);
+}
+
+static void ovnact_dhcp_relay_free(
+          struct ovnact_dhcp_relay *dhcp_relay OVS_UNUSED)
+{
+}
+
 static void
 parse_put_opts(struct action_context *ctx, const struct expr_field *dst,
                struct ovnact_put_opts *po, const struct hmap *gen_opts,
@@ -5312,6 +5422,12 @@  parse_set_action(struct action_context *ctx)
                    lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
             parse_chk_lb_aff(ctx, &lhs,
                     ovnact_put_CHK_LB_AFF(ctx->ovnacts));
+        } else if (lexer_match_id(ctx->lexer, "dhcp_relay_req_chk")) {
+            parse_dhcp_relay_chk(ctx, &lhs,
+                ovnact_put_DHCPV4_RELAY_REQ_CHK(ctx->ovnacts));
+        } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_chk")) {
+            parse_dhcp_relay_chk(ctx, &lhs,
+                ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
         } else {
             parse_assignment_action(ctx, false, &lhs);
         }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 4c26c6714..81ed824d0 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -2801,6 +2801,68 @@  tcp.flags = RST;
             use statistics of MAC cache.
           </p>
         </dd>
+
+        <dt><code><var>R</var> = dhcp_relay_req_chk(<var>relay-ip</var>,
+                                      <var>server-ip</var>);</code></dt>
+        <dd>
+          <p>
+            <b>Parameters</b>: Logical Router Port IP <var>relay-ip</var>, DHCP
+            Server IP <var>server-ip</var>.
+          </p>
+
+          <p>
+            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
+          </p>
+
+          <p>
+            This action executes on the source node on which the DHCP request
+            (DHCPDISCOVER or DHCPREQUEST) originated.
+          </p>
+
+          <p>
+            When this action applied successfully on the DHCP request packet,
+            it updates GIADDR in the DHCP packet with <var>relay-ip</var> and
+            stores 1 in R.
+          </p>
+
+          <p>
+            When this action failed to apply on the packet, it leaves the
+            packet unchanged and stores 0 in R.
+          </p>
+        </dd>
+
+        <dt><code><var>R</var> = dhcp_relay_resp_chk(<var>relay-ip</var>,
+                                      <var>server-ip</var>);</code></dt>
+        <dd>
+          <p>
+            <b>Parameters</b>: Logical Router Port IP <var>relay-ip</var>, DHCP
+            Server IP <var>server-ip</var>.
+          </p>
+
+          <p>
+            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
+          </p>
+
+          <p>
+            This action executes on the first node (Redirect Chassis node)
+            which processes the DHCP response(DHCPOFFER, DHCPACK) from the DHCP
+            server.
+          </p>
+
+          <p>
+            When this action applied successfully on the DHCP response packet,
+            it updates the destination MAC and destination IP in the packet and
+            stores 1 in R.
+            <var>relay-ip</var> and <var>server-ip</var> are used to validate
+            GIADDR and SERVER-ID in the DHCP response packet.
+          </p>
+
+          <p>
+            When this action failed to apply on the packet, it leaves the
+            packet unchanged and stores 0 in R.
+          </p>
+        </dd>
+
       </dl>
     </column>
 
diff --git a/tests/ovn.at b/tests/ovn.at
index dc6aafd53..1ad4159cf 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1653,6 +1653,40 @@  reg1[[0]] = put_dhcp_opts(offerip=1.2.3.4, domain_name=1.2.3.4);
 reg1[[0]] = put_dhcp_opts(offerip=1.2.3.4, domain_search_list=1.2.3.4);
     DHCPv4 option domain_search_list requires string value.
 
+#dhcp_relay_req_chk
+reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
+    encodes as controller(userdata=00.00.00.1c.00.00.00.00.80.01.08.08.00.00.00.07.c0.a8.01.01.ac.10.01.01,pause)
+
+reg9[[7]] = dhcp_relay_req_chk(192.168.1.1,172.16.1.1);
+    formats as reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
+    encodes as controller(userdata=00.00.00.1c.00.00.00.00.80.01.08.08.00.00.00.07.c0.a8.01.01.ac.10.01.01,pause)
+
+reg9[[7..8]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);
+    Cannot use 2-bit field reg9[[7..8]] where 1-bit field is required.
+
+reg9[[7]] = dhcp_relay_req_chk("192.168.1.1", "172.16.1.1");
+    Syntax error at `"192.168.1.1"' expecting IPv4 dhcp relay and server ips.
+
+reg9[[7]] = dhcp_relay_req_chk(192.168.1, 172.16.1.1);
+    Invalid numeric constant.
+
+#dhcp_relay_resp_chk
+reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
+    encodes as controller(userdata=00.00.00.1d.00.00.00.00.80.01.08.08.00.00.00.08.c0.a8.01.01.ac.10.01.01,pause)
+
+reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1,172.16.1.1);
+    formats as reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
+    encodes as controller(userdata=00.00.00.1d.00.00.00.00.80.01.08.08.00.00.00.08.c0.a8.01.01.ac.10.01.01,pause)
+
+reg9[[7..8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);
+    Cannot use 2-bit field reg9[[7..8]] where 1-bit field is required.
+
+reg9[[8]] = dhcp_relay_resp_chk("192.168.1.1", "172.16.1.1");
+    Syntax error at `"192.168.1.1"' expecting IPv4 dhcp relay and server ips.
+
+reg9[[8]] = dhcp_relay_resp_chk(192.168.1, 172.16.1.1);
+    Invalid numeric constant.
+
 # nd_ns
 nd_ns { nd.target = xxreg0; output; };
     encodes as controller(userdata=00.00.00.09.00.00.00.00.00.1c.00.18.00.80.00.00.00.00.00.00.00.01.de.10.80.00.3e.10.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.OFTABLE_SAVE_INPORT_HEX.00.00.00,pause)
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index ee086a7ae..0103253e1 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -2329,6 +2329,63 @@  execute_put_dhcp_opts(const struct ovnact_put_opts *pdo,
     execute_put_opts(pdo, name, uflow, super);
 }
 
+static void
+execute_dhcpv4_relay_req_chk(const struct ovnact_dhcp_relay *dr,
+                             struct flow *uflow, struct ovs_list *super)
+{
+    ovntrace_node_append(
+        super, OVNTRACE_NODE_ERROR,
+        "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */");
+
+    struct ds s = DS_EMPTY_INITIALIZER;
+    struct mf_subfield dst = expr_resolve_field(&dr->dst);
+    if (!mf_is_register(dst.field->id)) {
+        /* Format assignment. */
+        ds_clear(&s);
+        expr_field_format(&dr->dst, &s);
+        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
+                             "%s = 1", ds_cstr(&s));
+    }
+    ds_destroy(&s);
+
+    struct mf_subfield sf = expr_resolve_field(&dr->dst);
+    union mf_subvalue sv = { .u8_val = 1 };
+    mf_write_subfield_flow(&sf, &sv, uflow);
+}
+
+static void
+execute_dhcpv4_relay_resp_chk(const struct ovnact_dhcp_relay *dr,
+                              struct flow *uflow, struct ovs_list *super)
+{
+    ovntrace_node_append(
+        super, OVNTRACE_NODE_ERROR,
+        "/* We assume that this packet is DHCPOFFER or DHCPACK and "
+            "DHCP broadcast flag is set. Dest IP is set to broadcast. "
+            "Dest MAC is set to broadcast but in real network this is unicast "
+            "which is extracted from DHCP header. */");
+
+    /* Assume DHCP broadcast flag is set */
+    uflow->nw_dst = htonl(0xFFFFFFFF);
+    /* Dest MAC is set to broadcast but in real network this is unicast */
+    struct eth_addr bcast_mac = { .ea = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
+    uflow->dl_dst = bcast_mac;
+
+    struct ds s = DS_EMPTY_INITIALIZER;
+    struct mf_subfield dst = expr_resolve_field(&dr->dst);
+    if (!mf_is_register(dst.field->id)) {
+        /* Format assignment. */
+        ds_clear(&s);
+        expr_field_format(&dr->dst, &s);
+        ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
+                             "%s = 1", ds_cstr(&s));
+    }
+    ds_destroy(&s);
+
+    struct mf_subfield sf = expr_resolve_field(&dr->dst);
+    union mf_subvalue sv = { .u8_val = 1 };
+    mf_write_subfield_flow(&sf, &sv, uflow);
+}
+
 static void
 execute_put_nd_ra_opts(const struct ovnact_put_opts *pdo,
                        const char *name, struct flow *uflow,
@@ -3215,6 +3272,16 @@  trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
                                   "put_dhcpv6_opts", uflow, super);
             break;
 
+        case OVNACT_DHCPV4_RELAY_REQ_CHK:
+            execute_dhcpv4_relay_req_chk(ovnact_get_DHCPV4_RELAY_REQ_CHK(a),
+                                         uflow, super);
+            break;
+
+        case OVNACT_DHCPV4_RELAY_RESP_CHK:
+            execute_dhcpv4_relay_resp_chk(ovnact_get_DHCPV4_RELAY_RESP_CHK(a),
+                                          uflow, super);
+            break;
+
         case OVNACT_PUT_ND_RA_OPTS:
             execute_put_nd_ra_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
                                    "put_nd_ra_opts", uflow, super);