Message ID | 20240424095607.129155-2-naveen.yerramneni@nutanix.com |
---|---|
State | Accepted |
Headers | show |
Series | DHCP Relay Agent support for overlay subnets. | expand |
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 |
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 --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);
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(+)