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.yerramn...@nutanix.com> --- include/ovn/actions.h | 27 ++++++++ lib/actions.c | 149 ++++++++++++++++++++++++++++++++++++++++++ utilities/ovn-trace.c | 67 +++++++++++++++++++ 3 files changed, 243 insertions(+) diff --git a/include/ovn/actions.h b/include/ovn/actions.h index dcacbb1ff..8d0c6b9fa 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) \ @@ -396,6 +398,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 @@ -772,6 +783,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 71615fc53..d4f4ec2d0 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -2706,6 +2706,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, @@ -5406,6 +5549,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); } diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index e0f1c3ec9..dd7016fc3 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, @@ -3216,6 +3273,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 d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev