@@ -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. */
@@ -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);
}
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(+)