diff mbox series

[ovs-dev,v3,2/4] actions: DHCP Relay Agent support for overlay IPv4 subnets.

Message ID 20240319075458.49166-3-naveen.yerramneni@nutanix.com
State Superseded
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 March 19, 2024, 7:54 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         | 149 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 176 insertions(+)
diff mbox series

Patch

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index 49fb96fc6..a8e4393ed 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -95,6 +95,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)             \
@@ -395,6 +397,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
@@ -758,6 +769,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 a45874dfb..d55b5153f 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -2680,6 +2680,149 @@  ovnact_controller_event_free(struct ovnact_controller_event *event)
     free_gen_options(event->options, event->n_options);
 }
 
+static void
+format_DHCPV4_RELAY_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                struct ds *s)
+{
+    expr_field_format(&dhcp_relay->dst, s);
+    ds_put_format(s, " = dhcp_relay_req_chk("IP_FMT", "IP_FMT");",
+                  IP_ARGS(dhcp_relay->relay_ipv4),
+                  IP_ARGS(dhcp_relay->server_ipv4));
+}
+
+static void
+parse_dhcp_relay_req_chk(struct action_context *ctx,
+               const struct expr_field *dst,
+               struct ovnact_dhcp_relay *dhcp_relay)
+{
+    /* Skip dhcp_relay_req_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_REQ_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                    const struct ovnact_encode_params *ep,
+                    struct ofpbuf *ofpacts)
+{
+    struct mf_subfield dst = expr_resolve_field(&dhcp_relay->dst);
+    size_t oc_offset = encode_start_controller_op(
+                                            ACTION_OPCODE_DHCP_RELAY_REQ_CHK,
+                                            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_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                    struct ds *s)
+{
+    expr_field_format(&dhcp_relay->dst, s);
+    ds_put_format(s, " = dhcp_relay_resp_chk("IP_FMT", "IP_FMT");",
+                  IP_ARGS(dhcp_relay->relay_ipv4),
+                  IP_ARGS(dhcp_relay->server_ipv4));
+}
+
+static void
+parse_dhcp_relay_resp_chk(struct action_context *ctx,
+               const struct expr_field *dst,
+               struct ovnact_dhcp_relay *dhcp_relay)
+{
+    /* Skip 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_RESP_CHK(const struct ovnact_dhcp_relay *dhcp_relay,
+                    const struct ovnact_encode_params *ep,
+                    struct ofpbuf *ofpacts)
+{
+    struct mf_subfield dst = expr_resolve_field(&dhcp_relay->dst);
+    size_t oc_offset = encode_start_controller_op(
+                                ACTION_OPCODE_DHCP_RELAY_RESP_CHK,
+                                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 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,
@@ -5384,6 +5527,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_req_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_resp_chk(ctx, &lhs,
+                    ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts));
         } else {
             parse_assignment_action(ctx, false, &lhs);
         }