On Sun, Mar 3, 2024 at 11:20 PM Naveen Yerramneni
<[email protected]> wrote:
>
> This patch contains changes to enable DHCP Relay Agent support for
> overlay subnets.
>
> USE CASE:
> ----------
> - Enable IP address assignment for overlay subnets from the centralized
> DHCP server present in the underlay network.
>
> PREREQUISITES
> --------------
> - Logical Router Port IP should be assigned (statically) from the same
> overlay subnet which is managed by DHCP server.
> - LRP IP is used for GIADRR field when relaying the DHCP packets and
> also same IP needs to be configured as default gateway for the overlay subnet.
> - Overlay subnets managed by external DHCP server are expected to be
> directly reachable from the underlay network.
>
> EXPECTED PACKET FLOW:
> ----------------------
> Following is the expected packet flow inorder to support DHCP rleay
> functionality in OVN.
> 1. DHCP client originates DHCP discovery (broadcast).
> 2. DHCP relay (running on the OVN) receives the broadcast and forwards
> the packet to the DHCP server by converting it to unicast.
> While forwarding the packet, it updates the GIADDR in DHCP header to
> its interface IP on which DHCP packet is received and increments hop count.
> 3. DHCP server uses GIADDR field to decide the IP address pool from
> which IP has to be assigned and DHCP offer is sent to the same IP (GIADDR).
> 4. DHCP relay agent forwards the offer to the client.
> 5. DHCP client sends DHCP request (broadcast) packet.
> 6. DHCP relay (running on the OVN) receives the broadcast and forwards
> the packet to the DHCP server by converting it to unicast.
> While forwarding the packet, it updates the GIADDR in DHCP header to
> its interface IP on which DHCP packet is received.
> 7. DHCP Server sends the ACK packet.
> 8. DHCP relay agent forwards the ACK packet to the client.
> 9. All the future renew/release packets are directly exchanged between
> DHCP client and DHCP server.
>
> OVN DHCP RELAY PACKET FLOW:
> ----------------------------
> To add DHCP Relay support on OVN, we need to replicate all the behavior
> described above using distributed logical switch and logical router.
> At, highlevel packet flow is distributed among Logical Switch and Logical
> Router on source node (where VM is deployed) and redirect chassis(RC) node.
> 1. Request packet gets processed on the source node where VM is
> deployed and relays the packet to DHCP server.
> 2. Response packet is first processed on RC node (which first recieves
> the packet from underlay network). RC node forwards the packet to the right
> node by filling in the dest MAC and IP.
>
> OVN Packet flow with DHCP relay is explained below.
> 1. DHCP client (VM) sends the DHCP discover packet (broadcast).
> 2. Logical switch converts the packet to L2 unicast by setting the
> destination MAC to LRP's MAC
> 3. Logical Router receives the packet and redirects it to the OVN
> controller.
> 4. OVN controller updates the required information(GIADDR, HOP count)
> in the DHCP payload after doing the required checks. If any check fails,
> packet is dropped.
> 5. Logical Router converts the packet to L3 unicast and forwards it to
> the server. This packets gets routed like any other packet (via RC node).
> 6. Server replies with DHCP offer.
> 7. RC node processes the DHCP offer and forwards it to the OVN
> controller.
> 8. OVN controller does sanity checks and updates the destination MAC
> (available in DHCP header), destination IP (available in DHCP header) and
> reinjects the packet to datapath.
> If any check fails, packet is dropped.
> 9. Logical router updates the source IP and port and forwards the
> packet to logical switch.
> 10. Logical switch delivers the packet to the DHCP client.
> 11. Similar steps are performed for Request and Ack packets.
> 12. All the future renew/release packets are directly exchanged between
> DHCP client and DHCP server
>
> 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.
>
> FLOWS
> -----
> Following are the flows added when DHCP Relay is configured on one
> overlay subnet, one additonal flow is added in ls_in_l2_lkup table for each
> VM part of the subnet.
>
> 1. table=27(ls_in_l2_lkup ), priority=100 , match=(inport ==
> <vm_port> && eth.src == <vm_mac> && ip4.src == 0.0.0.0 && ip4.dst ==
> 255.255.255.255 && udp.src == 68 && udp.dst == 67),
> action=(eth.dst=<lrp_mac>;outport=<lrp>;next;/* DHCP_RELAY_REQ */)
> 2. table=3 (lr_in_ip_input ), priority=110 , match=(inport ==
> <lrp> && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 &&
> udp.dst == 67),
> action=(reg9[7] = dhcp_relay_req_chk(<lrp_ip>,
> <dhcp_server_ip>);next; /* DHCP_RELAY_REQ */)
> 3. table=3 (lr_in_ip_input ), priority=110 , match=(ip4.src ==
> <dhcp_server> && ip4.dst == <lrp> && udp.src == 67 && udp.dst == 67),
> action=(next;/* DHCP_RELAY_RESP */)
> 4. table=4 (lr_in_dhcp_relay_req), priority=100 , match=(inport ==
> "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68
> && udp.dst == 67 && reg9[7]),
> action=(ip4.src=<lrp>;ip4.dst=<dhcp_server>;udp.src=67;next; /*
> DHCP_RELAY_REQ */)
> 5. table=4 (lr_in_dhcp_relay_req), priority=1 , match=(inport ==
> <lrp> && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 &&
> udp.dst == 67 && reg9[7] == 0),
> action=(drop; /* DHCP_RELAY_REQ */)
> 6. table=18(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src
> == <dhcp_server> && ip4.dst == <lrp> && udp.src == 67 && udp.dst == 67),
> action=(reg2 = ip4.dst;reg9[8] = dhcp_relay_resp_chk(<lrp_ip>,
> <dhcp_server_ip>);next;/* DHCP_RELAY_RESP */)
> 7. table=19(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src ==
> <dhcp_server> && reg2 == <lrp_ip> && udp.src == 67 && udp.dst == 67 &&
> reg9[8]),
> action=(ip4.src=<lrp>;udp.dst=68;outport=<lrp>;output; /*
> DHCP_RELAY_RESP */)
> 8. table=19(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src ==
> <dhcp_server> && reg2 == <lrp_ip> && udp.src == 67 && udp.dst == 67 &&
> reg9[8] == 0), action=(drop; /* DHCP_RELAY_RESP */)
>
> NEW PIPELINE STAGES
> -------------------
> Following stage is added for DHCP relay feature. Some of the flows are
> fitted into the existing pipeline tages.
> 1. lr_in_dhcp_relay_req
> - This stage process the DHCP request packets coming from DHCP
> clients.
> - DHCP request packets for which dhcp_relay_req_chk action (which
> gets applied in ip input stage) is successful are forwarded to DHCP server.
> - DHCP request packets for which dhcp_relay_req_chk action is
> unsuccessful gets dropped.
> 2. lr_in_dhcp_relay_resp_chk
> - This stage applied the dhcp_relay_resp_chk action for DHCP
> response packets coming from the DHCP server.
> 3. lr_in_dhcp_relay_resp
> - DHCP response packets for which dhcp_relay_resp_chk is sucessful
> are forwarded to the DHCP clients.
> - DHCP response packets for which dhcp_relay_resp_chk is
> unsucessful gets dropped.
>
> REGISTRY USAGE
> ---------------
> - reg9[7] : To store the result of dhcp_relay_req_chk action.
> - reg9[8] : To store the result of dhcp_relay_resp_chk action.
> - reg2 : To store the original dest ip for DHCP response packets.
> This is required to properly match the packets in
> lr_in_dhcp_relay_resp stage since dhcp_relay_resp_chk action
> changes the dest ip.
>
> NB SCHEMA CHANGES
> ----------------
> 1. New DHCP_Relay table
> "DHCP_Relay": {
> "columns": {
> "name": {"type": "string"},
> "servers": {"type": {"key": "string",
> "min": 0,
> "max": 1}},
> "external_ids": {
> "type": {"key": "string", "value": "string",
> "min": 0, "max": "unlimited"}}},
> "options": {"type": {"key": "string", "value": "string",
> "min": 0, "max": "unlimited"}},
> "isRoot": true},
> 2. New column to Logical_Router_Port table
> "dhcp_relay": {"type": {"key": {"type": "uuid",
> "refTable": "DHCP_Relay",
> "refType": "strong"},
> "min": 0,
> "max": 1}},
>
> Commands to enable the feature:
> ------------------------------
> ovn-nbctl create DHCP_Relay name=<name> servers=<dhcp_server_ip>
> ovn-nbctl set Logical_Router_port <lrp> dhcp_relay=<relay_uuid>
> ovn-nbctl set Logical_Switch <ls>
> other_config:dhcp_relay_port=<router_patch_port>
>
> Example:
> -------
> ovn-nbctl ls-add ls0
> ovn-nbctl lsp-add ls0 vif0
> ovn-nbctl lsp-set-addresses vif0 <MAC> #Only MAC address has to be
> specified when logical ports are created.
> ovn-nbctl lsp-add ls0 lrp1-attachment
> ovn-nbctl lsp-set-type lrp1-attachment router
> ovn-nbctl lsp-set-addresses lrp1-attachment
> ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1
> ovn-nbctl lr-add lr0
> ovn-nbctl lrp-add lr0 lrp1 <MAC> <GATEWAY_IP/Prefix> #GATEWAY IP is set
> in GIADDR field when relaying the DHCP requests to server.
> ovn-nbctl lrp-add lr0 lrp-ext <MAC> <GATEWAY_IP/Prefix>
> ovn-nbctl ls-add ls-ext
> ovn-nbctl lsp-add ls-ext lrp-ext-attachment
> ovn-nbctl lsp-set-type lrp-ext-attachment router
> ovn-nbctl lsp-set-addresses lrp-ext-attachment
> ovn-nbctl lsp-set-options lrp-ext-attachment router-port=lrp-ext
> ovn-nbctl lsp-add ls-ext ln_port
> ovn-nbctl lsp-set-addresses ln_port unknown
> ovn-nbctl lsp-set-type ln_port localnet
> ovn-nbctl lsp-set-options ln_port network_name=physnet1
> # Enable DHCP Relay feature
> ovn-nbctl create DHCP_Relay name=dhcp_relay_test servers=<dhcp_server_ip>
> ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=<relay_uuid>
> ovn-nbctl set Logical_Switch ls0
> other_config:dhcp_relay_port=lrp1-attachment
>
> Limitations:
> ------------
> - All OVN features that needs IP address to be configured on logical
> port (like proxy arp, etc) will not be supported for overlay subnets on which
> DHCP relay is enabled.
>
> References:
> ----------
> - rfc1541, rfc1542, rfc2131
>
> Signed-off-by: Naveen Yerramneni <[email protected]>
> Co-authored-by: Huzaifa Calcuttawala <[email protected]>
> Signed-off-by: Huzaifa Calcuttawala <[email protected]>
> CC: Mary Manohar <[email protected]>
> ---
> V2:
> Addressed review comments from Numan.
Thanks for the v2. This patch is failing compilation with gcc and
address sanitizer configured (./configure --enable-Werror
--enable-sparse 'CFLAGS=-g -fsanitize=address')
---------------------------
../utilities/ovn-trace.c:2368:19: error: incorrect type in assignment
(different base types)
../utilities/ovn-trace.c:2368:19: expected restricted ovs_be32
[usertype] nw_dst
../utilities/ovn-trace.c:2368:19: got unsigned int
depbase=`echo utilities/ovn-appctl.o | sed 's|[^/]*$|.deps/&|;s|\.o$||'`;\
--
../controller/pinctrl.c:2174:14: error: incorrect type in argument 2
(different base types)
../controller/pinctrl.c:2174:14: expected restricted ovs_be16
[usertype] old_u16
../controller/pinctrl.c:2174:14: got unsigned short [usertype]
../controller/pinctrl.c:2174:38: error: incorrect type in argument 3
(different base types)
../controller/pinctrl.c:2174:38: expected restricted ovs_be16
[usertype] new_u16
../controller/pinctrl.c:2174:38: got unsigned short [usertype]
make[1]: *** [Makefile:2371: controller/pinctrl.o] Error 1
--------------------------------
I'd suggest split this patch into a 3 patch series.
In patch 1 - pinctrl.c changes for the action opcodes
ACTION_OPCODE_DHCP_RELAY_REQ_CHK and ACTION_OPCODE_DHCP_RELAY_RESP_CHK
In patch 2 - Add the OVN logical actions - dhcp_relay_req_chk and
dhcp_relay_resp_chk and the corresponding tests.
and finally northd changes and test cases in patch 3.
There are indentation issues with the patch. Please correct me as per
the present code/coding guidelines.
Please see below for a few comments.
Thanks
Numan
> ---
> controller/pinctrl.c | 596 +++++++++++++++++++++++++++++++++++++-----
> include/ovn/actions.h | 27 ++
> lib/actions.c | 149 +++++++++++
> lib/ovn-l7.h | 2 +
> northd/northd.c | 265 ++++++++++++++++++-
> northd/northd.h | 41 +--
> ovn-nb.ovsschema | 19 +-
> ovn-nb.xml | 39 +++
> tests/atlocal.in | 3 +
> tests/ovn-northd.at | 38 +++
> tests/ovn.at | 293 +++++++++++++++++++--
> tests/system-ovn.at | 148 +++++++++++
> utilities/ovn-trace.c | 67 +++++
> 13 files changed, 1576 insertions(+), 111 deletions(-)
>
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 98b29de9f..a776ac7c5 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -1909,6 +1909,514 @@ is_dhcp_flags_broadcast(ovs_be16 flags)
> return flags & htons(DHCP_BROADCAST_FLAG);
> }
>
> +static const char *dhcp_msg_str[] = {
> +[0] = "INVALID",
> +[DHCP_MSG_DISCOVER] = "DISCOVER",
> +[DHCP_MSG_OFFER] = "OFFER",
> +[DHCP_MSG_REQUEST] = "REQUEST",
> +[OVN_DHCP_MSG_DECLINE] = "DECLINE",
> +[DHCP_MSG_ACK] = "ACK",
> +[DHCP_MSG_NAK] = "NAK",
> +[OVN_DHCP_MSG_RELEASE] = "RELEASE",
> +[OVN_DHCP_MSG_INFORM] = "INFORM"
> +};
> +
> +static bool
> +dhcp_relay_is_msg_type_supported(uint8_t msg_type)
> +{
> + return (msg_type >= DHCP_MSG_DISCOVER && msg_type <=
> OVN_DHCP_MSG_RELEASE);
> +}
> +
> +static const char *dhcp_msg_str_get(uint8_t msg_type)
> +{
> + if (!dhcp_relay_is_msg_type_supported(msg_type)) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "Unknown DHCP msg type: %u", msg_type);
> + return "UNKNOWN";
> + }
> + return dhcp_msg_str[msg_type];
> +}
> +
> +static const struct dhcp_header *
> +dhcp_get_hdr_from_pkt(struct dp_packet *pkt_in, const char **in_dhcp_pptr,
> + const char *end)
> +{
> + /* Validate the DHCP request packet.
> + * Format of the DHCP packet is
> + *
> ------------------------------------------------------------------------
> + *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var
> len)|
> + *
> ------------------------------------------------------------------------
> + */
> +
> + *in_dhcp_pptr = dp_packet_get_udp_payload(pkt_in);
> + if (*in_dhcp_pptr == NULL) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP: Invalid or incomplete DHCP packet
> received");
> + return NULL;
> + }
> +
> + const struct dhcp_header *dhcp_hdr
> + = (const struct dhcp_header *) *in_dhcp_pptr;
> + (*in_dhcp_pptr) += sizeof *dhcp_hdr;
> + if (*in_dhcp_pptr > end) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP: Invalid or incomplete DHCP packet received,
> "
> + "bad data length");
> + return NULL;
> + }
> +
> + if (dhcp_hdr->htype != 0x1) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP: Packet is recieved with "
> + "unsupported hardware type");
> + return NULL;
> + }
> +
> + if (dhcp_hdr->hlen != 0x6) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP: Packet is recieved with "
> + "unsupported hardware length");
> + return NULL;
> + }
> +
> + /* DHCP options follow the DHCP header. The first 4 bytes of the DHCP
> + * options is the DHCP magic cookie followed by the actual DHCP options.
> + */
> + ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE);
> + if ((*in_dhcp_pptr) + sizeof magic_cookie > end ||
> + get_unaligned_be32((const void *) (*in_dhcp_pptr)) != magic_cookie) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP: Magic cookie not present in the DHCP
> packet");
> + return NULL;
> + }
> +
> + (*in_dhcp_pptr) += sizeof magic_cookie;
> +
> + return dhcp_hdr;
> +}
> +
> +static void
> +dhcp_parse_options(const char **in_dhcp_pptr, const char *end,
> + const uint8_t **dhcp_msg_type_pptr, ovs_be32 *request_ip_ptr,
> + bool *ipxe_req_ptr, ovs_be32 *server_id_ptr,
> + ovs_be32 *netmask_ptr, ovs_be32 *router_ip_ptr)
> +{
> + while ((*in_dhcp_pptr) < end) {
> + const struct dhcp_opt_header *in_dhcp_opt =
> + (const struct dhcp_opt_header *) *in_dhcp_pptr;
> + if (in_dhcp_opt->code == DHCP_OPT_END) {
> + break;
> + }
> + if (in_dhcp_opt->code == DHCP_OPT_PAD) {
> + (*in_dhcp_pptr) += 1;
> + continue;
> + }
> + (*in_dhcp_pptr) += sizeof *in_dhcp_opt;
> + if ((*in_dhcp_pptr) > end) {
> + break;
> + }
> + (*in_dhcp_pptr) += in_dhcp_opt->len;
> + if ((*in_dhcp_pptr) > end) {
> + break;
> + }
> +
> + switch (in_dhcp_opt->code) {
> + case DHCP_OPT_MSG_TYPE:
> + if (dhcp_msg_type_pptr && in_dhcp_opt->len == 1) {
> + *dhcp_msg_type_pptr = DHCP_OPT_PAYLOAD(in_dhcp_opt);
> + }
> + break;
> + case DHCP_OPT_REQ_IP:
> + if (request_ip_ptr && in_dhcp_opt->len == 4) {
> + *request_ip_ptr = get_unaligned_be32(
> + DHCP_OPT_PAYLOAD(in_dhcp_opt));
> + }
> + break;
> + case OVN_DHCP_OPT_CODE_SERVER_ID:
> + if (server_id_ptr && in_dhcp_opt->len == 4) {
> + *server_id_ptr = get_unaligned_be32(
> + DHCP_OPT_PAYLOAD(in_dhcp_opt));
> + }
> + break;
> + case OVN_DHCP_OPT_CODE_NETMASK:
> + if (netmask_ptr && in_dhcp_opt->len == 4) {
> + *netmask_ptr = get_unaligned_be32(
> + DHCP_OPT_PAYLOAD(in_dhcp_opt));
> + }
> + break;
> + case OVN_DHCP_OPT_CODE_ROUTER_IP:
> + if (router_ip_ptr && in_dhcp_opt->len == 4) {
> + *router_ip_ptr = get_unaligned_be32(
> + DHCP_OPT_PAYLOAD(in_dhcp_opt));
> + }
> + break;
> + case DHCP_OPT_ETHERBOOT:
> + if (ipxe_req_ptr) {
> + *ipxe_req_ptr = true;
> + }
> + break;
> + default:
> + break;
> + }
> + }
> +}
> +
> +/* Called with in the pinctrl_handler thread context. */
> +static void
> +pinctrl_handle_dhcp_relay_req_chk(
> + struct rconn *swconn,
> + struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
> + struct ofpbuf *userdata,
> + struct ofpbuf *continuation)
> +{
> + enum ofp_version version = rconn_get_version(swconn);
> + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
> + struct dp_packet *pkt_out_ptr = NULL;
> + uint32_t success = 0;
> +
> + /* Parse result field. */
> + const struct mf_field *f;
> + enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
> + if (ofperr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: bad result OXM (%s)",
> + ofperr_to_string(ofperr));
> + goto exit;
> + }
> + ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
> + /* Check that the result is valid and writable. */
> + struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1
> };
> + ofperr = mf_check_dst(&dst, NULL);
> + if (ofperr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: bad result bit (%s)",
> + ofperr_to_string(ofperr));
> + goto exit;
> + }
> +
> + /* Parse relay IP and server IP. */
> + ovs_be32 *relay_ip = ofpbuf_try_pull(userdata, sizeof *relay_ip);
> + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof *server_ip);
> + if (!relay_ip || !server_ip) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: relay ip or server ip "
> + "not present in the userdata");
> + goto exit;
> + }
> +
> + size_t in_l4_size = dp_packet_l4_size(pkt_in);
> + const char *end = (char *) dp_packet_l4(pkt_in) + in_l4_size;
> + const char *in_dhcp_ptr = NULL;
> + const struct dhcp_header *in_dhcp_data
> + = dhcp_get_hdr_from_pkt(pkt_in, &in_dhcp_ptr, end);
> +
> + if (!in_dhcp_data) {
> + goto exit;
> + }
> + ovs_assert(in_dhcp_ptr);
> +
> + if (in_dhcp_data->op != DHCP_OP_REQUEST) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: invalid opcode in the "
> + "DHCP packet: %d", in_dhcp_data->op);
> + goto exit;
> + }
> +
> + if (in_dhcp_data->giaddr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: giaddr is already set");
> + goto exit;
> + }
> +
> + const uint8_t *in_dhcp_msg_type = NULL;
> + ovs_be32 request_ip = in_dhcp_data->ciaddr;
> +
> + dhcp_parse_options(&in_dhcp_ptr, end,
> + &in_dhcp_msg_type, &request_ip, NULL, NULL, NULL, NULL);
> +
> + /* Check whether the DHCP Message Type (opt 53) is present or not */
> + if (!in_dhcp_msg_type) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ_CHK: missing message type");
> + goto exit;
> + }
> +
> + /* Relay the DHCP request packet */
> + uint16_t new_l4_size = in_l4_size;
> + size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
> +
> + struct dp_packet pkt_out;
> + dp_packet_init(&pkt_out, new_packet_size);
> + dp_packet_clear(&pkt_out);
> + dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
> + pkt_out_ptr = &pkt_out;
> +
> + /* Copy the L2 and L3 headers from the pkt_in as they would remain same*/
> + dp_packet_put(
> + &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs);
> +
> + pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
> + pkt_out.l2_pad_size = pkt_in->l2_pad_size;
> + pkt_out.l3_ofs = pkt_in->l3_ofs;
> + pkt_out.l4_ofs = pkt_in->l4_ofs;
> +
> + struct udp_header *udp = dp_packet_put(
> + &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);
> +
> + struct dhcp_header *dhcp_data = dp_packet_put(&pkt_out,
> + dp_packet_pull(pkt_in, new_l4_size - UDP_HEADER_LEN),
> + new_l4_size - UDP_HEADER_LEN);
> +
> + uint8_t hops = dhcp_data->hops;
> + dhcp_data->hops = hops + 1;
> + if (udp->udp_csum) {
> + udp->udp_csum = recalc_csum16(udp->udp_csum,
> + (uint16_t) (hops << 8), (uint16_t) (dhcp_data->hops << 8));
recalc_csum16() expects all the arguments of type 'ovs_be16' but
you're casting to uint16_t.
That's why the compilation is failing.
> + }
> +
> + dhcp_data->giaddr = *relay_ip;
> + if (udp->udp_csum) {
> + udp->udp_csum = recalc_csum32(udp->udp_csum,
> + 0, dhcp_data->giaddr);
> + }
> + pin->packet = dp_packet_data(&pkt_out);
> + pin->packet_len = dp_packet_size(&pkt_out);
> +
> + /* Log the DHCP message. */
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
> + VLOG_INFO_RL(&rl, "DHCP_RELAY_REQ_CHK:: MSG_TYPE:%s MAC:"ETH_ADDR_FMT
> + " XID:%u"
> + " REQ_IP:"IP_FMT
> + " GIADDR:"IP_FMT
> + " SERVER_ADDR:"IP_FMT,
> + dhcp_msg_str_get(*in_dhcp_msg_type),
> + ETH_ADDR_BYTES_ARGS(dhcp_data->chaddr),
> ntohl(dhcp_data->xid),
> + IP_ARGS(request_ip), IP_ARGS(dhcp_data->giaddr),
> + IP_ARGS(*server_ip));
> + success = 1;
> +exit:
> + if (!ofperr) {
> + union mf_subvalue sv;
> + sv.u8_val = success;
> + mf_write_subfield(&dst, &sv, &pin->flow_metadata);
> + }
> + queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
> + if (pkt_out_ptr) {
> + dp_packet_uninit(pkt_out_ptr);
> + }
> +}
> +
> +/* Called with in the pinctrl_handler thread context. */
> +static void
> +pinctrl_handle_dhcp_relay_resp_chk(
> + struct rconn *swconn,
> + struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
> + struct ofpbuf *userdata,
> + struct ofpbuf *continuation)
> +{
> + enum ofp_version version = rconn_get_version(swconn);
> + enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
> + struct dp_packet *pkt_out_ptr = NULL;
> + uint32_t success = 0;
> +
> + /* Parse result field. */
> + const struct mf_field *f;
> + enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
> + if (ofperr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP_CHK: bad result OXM (%s)",
> + ofperr_to_string(ofperr));
> + goto exit;
> + }
> + ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
> + /* Check that the result is valid and writable. */
> + struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1
> };
> + ofperr = mf_check_dst(&dst, NULL);
> + if (ofperr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP_CHK: bad result bit (%s)",
> + ofperr_to_string(ofperr));
> + goto exit;
> + }
> +
> + /* Parse relay IP and server IP. */
> + ovs_be32 *relay_ip = ofpbuf_try_pull(userdata, sizeof *relay_ip);
> + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof *server_ip);
> + if (!relay_ip || !server_ip) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP_CHK: relay ip or server ip "
> + "not present in the userdata");
> + goto exit;
> + }
> +
> + size_t in_l4_size = dp_packet_l4_size(pkt_in);
> + const char *end = (char *) dp_packet_l4(pkt_in) + in_l4_size;
> + const char *in_dhcp_ptr = NULL;
> + const struct dhcp_header *in_dhcp_data
> + = dhcp_get_hdr_from_pkt(pkt_in, &in_dhcp_ptr, end);
> +
> + if (!in_dhcp_data) {
> + goto exit;
> + }
> + ovs_assert(in_dhcp_ptr);
> +
> + if (in_dhcp_data->op != DHCP_OP_REPLY) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP_CHK: invalid opcode "
> + "in the packet: %d", in_dhcp_data->op);
> + goto exit;
> + }
> +
> + if (!in_dhcp_data->giaddr) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP_CHK: giaddr is "
> + "not set in request");
> + goto exit;
> + }
> +
> + ovs_be32 giaddr = in_dhcp_data->giaddr;
> + ovs_be32 yiaddr = in_dhcp_data->yiaddr;
> + ovs_be32 server_id = 0, netmask = 0, router_ip = 0;
> + const uint8_t *in_dhcp_msg_type = NULL;
> +
> + dhcp_parse_options(&in_dhcp_ptr, end,
> + &in_dhcp_msg_type, NULL, NULL, &server_id, &netmask, &router_ip);
> +
> + /* Check whether the DHCP Message Type (opt 53) is present or not */
> + if (!in_dhcp_msg_type) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: missing message type");
> + goto exit;
> + }
> +
> + if (!server_id) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: missing server identifier");
> + goto exit;
> + }
> +
> + if (server_id != *server_ip) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: server identifier mismatch");
> + goto exit;
> + }
> +
> + if (giaddr != *relay_ip) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: giaddr mismatch");
> + goto exit;
> + }
> +
> + if (*in_dhcp_msg_type == DHCP_MSG_OFFER ||
> + *in_dhcp_msg_type == DHCP_MSG_ACK) {
> + if ((yiaddr & netmask) != (giaddr & netmask)) {
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_INFO_RL(&rl, "DHCP_RELAY_RESP_CHK::"
> + " Allocated ip adress and giaddr are not in same
> subnet."
> + " MSG_TYPE:%s MAC:"ETH_ADDR_FMT
> + " XID:%u"
> + " YIADDR:"IP_FMT
> + " GIADDR:"IP_FMT
> + " SERVER_ADDR:"IP_FMT,
> + dhcp_msg_str_get(*in_dhcp_msg_type),
> + ETH_ADDR_BYTES_ARGS(in_dhcp_data->chaddr),
> + ntohl(in_dhcp_data->xid),
> + IP_ARGS(yiaddr),
> + IP_ARGS(giaddr), IP_ARGS(server_id));
> + goto exit;
> + }
> +
> + if (router_ip && router_ip != giaddr) {
> + /* Log the default gateway mismatch and
> + * continue with rest of the processing */
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> + VLOG_INFO_RL(&rl, "DHCP_RELAY_RESP_CHK::"
> + " Router ip adress and giaddr are not same."
> + " MSG_TYPE:%s MAC:"ETH_ADDR_FMT
> + " XID:%u"
> + " YIADDR:"IP_FMT
> + " GIADDR:"IP_FMT
> + " SERVER_ADDR:"IP_FMT,
> + dhcp_msg_str_get(*in_dhcp_msg_type),
> + ETH_ADDR_BYTES_ARGS(in_dhcp_data->chaddr),
> + ntohl(in_dhcp_data->xid),
> + IP_ARGS(yiaddr),
> + IP_ARGS(giaddr), IP_ARGS(server_id));
> + }
> + }
> +
> + /* Update destination MAC & IP so that the packet is forward to the
> + * right destination node.
> + */
> + uint16_t new_l4_size = in_l4_size;
> + size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
> +
> + struct dp_packet pkt_out;
> + dp_packet_init(&pkt_out, new_packet_size);
> + dp_packet_clear(&pkt_out);
> + dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
> + pkt_out_ptr = &pkt_out;
> +
> + /* Copy the L2 and L3 headers from the pkt_in as they would remain same*/
> + struct eth_header *eth = dp_packet_put(
> + &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs);
> +
> + pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
> + pkt_out.l2_pad_size = pkt_in->l2_pad_size;
> + pkt_out.l3_ofs = pkt_in->l3_ofs;
> + pkt_out.l4_ofs = pkt_in->l4_ofs;
> +
> + struct udp_header *udp = dp_packet_put(
> + &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);
> +
> + struct dhcp_header *dhcp_data = dp_packet_put(
> + &pkt_out, dp_packet_pull(pkt_in, new_l4_size - UDP_HEADER_LEN),
> + new_l4_size - UDP_HEADER_LEN);
> + memcpy(ð->eth_dst, dhcp_data->chaddr, sizeof(eth->eth_dst));
> +
> + /* Send a broadcast IP frame when BROADCAST flag is set. */
> + struct ip_header *out_ip = dp_packet_l3(&pkt_out);
> + ovs_be32 ip_dst;
> + ovs_be32 ip_dst_orig = get_16aligned_be32(&out_ip->ip_dst);
> + if (!is_dhcp_flags_broadcast(dhcp_data->flags)) {
> + ip_dst = dhcp_data->yiaddr;
> + } else {
> + ip_dst = htonl(0xffffffff);
> + }
> + put_16aligned_be32(&out_ip->ip_dst, ip_dst);
> + out_ip->ip_csum = recalc_csum32(out_ip->ip_csum,
> + ip_dst_orig, ip_dst);
> + if (udp->udp_csum) {
> + udp->udp_csum = recalc_csum32(udp->udp_csum,
> + ip_dst_orig, ip_dst);
> + }
> + pin->packet = dp_packet_data(&pkt_out);
> + pin->packet_len = dp_packet_size(&pkt_out);
> +
> + /* Log the DHCP message. */
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
> + VLOG_INFO_RL(&rl, "DHCP_RELAY_RESP_CHK:: MSG_TYPE:%s MAC:"ETH_ADDR_FMT
> + " XID:%u"
> + " YIADDR:"IP_FMT
> + " GIADDR:"IP_FMT
> + " SERVER_ADDR:"IP_FMT,
> + dhcp_msg_str_get(*in_dhcp_msg_type),
> + ETH_ADDR_BYTES_ARGS(dhcp_data->chaddr), ntohl(dhcp_data->xid),
> + IP_ARGS(yiaddr),
> + IP_ARGS(giaddr), IP_ARGS(server_id));
> + success = 1;
> +exit:
> + if (!ofperr) {
> + union mf_subvalue sv;
> + sv.u8_val = success;
> + mf_write_subfield(&dst, &sv, &pin->flow_metadata);
> + }
> + queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
> + if (pkt_out_ptr) {
> + dp_packet_uninit(pkt_out_ptr);
> + }
> +}
> +
> /* Called with in the pinctrl_handler thread context. */
> static void
> pinctrl_handle_put_dhcp_opts(
> @@ -1956,30 +2464,16 @@ pinctrl_handle_put_dhcp_opts(
> goto exit;
> }
>
> - /* Validate the DHCP request packet.
> - * Format of the DHCP packet is
> - *
> ------------------------------------------------------------------------
> - *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var
> len)|
> - *
> ------------------------------------------------------------------------
> - */
> -
> const char *end = (char *)dp_packet_l4(pkt_in) +
> dp_packet_l4_size(pkt_in);
> - const char *in_dhcp_ptr = dp_packet_get_udp_payload(pkt_in);
> - if (!in_dhcp_ptr) {
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> - VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet received");
> - goto exit;
> - }
> -
> + const char *in_dhcp_ptr = NULL;
> const struct dhcp_header *in_dhcp_data
> - = (const struct dhcp_header *) in_dhcp_ptr;
> - in_dhcp_ptr += sizeof *in_dhcp_data;
> - if (in_dhcp_ptr > end) {
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> - VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet received, "
> - "bad data length");
> + = dhcp_get_hdr_from_pkt(pkt_in, &in_dhcp_ptr, end);
> +
> + if (!in_dhcp_data) {
> goto exit;
> }
> + ovs_assert(in_dhcp_ptr);
> +
> if (in_dhcp_data->op != DHCP_OP_REQUEST) {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> VLOG_WARN_RL(&rl, "Invalid opcode in the DHCP packet: %d",
> @@ -1987,58 +2481,11 @@ pinctrl_handle_put_dhcp_opts(
> goto exit;
> }
>
> - /* DHCP options follow the DHCP header. The first 4 bytes of the DHCP
> - * options is the DHCP magic cookie followed by the actual DHCP options.
> - */
> - ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE);
> - if (in_dhcp_ptr + sizeof magic_cookie > end ||
> - get_unaligned_be32((const void *) in_dhcp_ptr) != magic_cookie) {
> - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> - VLOG_WARN_RL(&rl, "DHCP magic cookie not present in the DHCP
> packet");
> - goto exit;
> - }
> - in_dhcp_ptr += sizeof magic_cookie;
> -
> bool ipxe_req = false;
> const uint8_t *in_dhcp_msg_type = NULL;
> ovs_be32 request_ip = in_dhcp_data->ciaddr;
> - while (in_dhcp_ptr < end) {
> - const struct dhcp_opt_header *in_dhcp_opt =
> - (const struct dhcp_opt_header *)in_dhcp_ptr;
> - if (in_dhcp_opt->code == DHCP_OPT_END) {
> - break;
> - }
> - if (in_dhcp_opt->code == DHCP_OPT_PAD) {
> - in_dhcp_ptr += 1;
> - continue;
> - }
> - in_dhcp_ptr += sizeof *in_dhcp_opt;
> - if (in_dhcp_ptr > end) {
> - break;
> - }
> - in_dhcp_ptr += in_dhcp_opt->len;
> - if (in_dhcp_ptr > end) {
> - break;
> - }
> -
> - switch (in_dhcp_opt->code) {
> - case DHCP_OPT_MSG_TYPE:
> - if (in_dhcp_opt->len == 1) {
> - in_dhcp_msg_type = DHCP_OPT_PAYLOAD(in_dhcp_opt);
> - }
> - break;
> - case DHCP_OPT_REQ_IP:
> - if (in_dhcp_opt->len == 4) {
> - request_ip =
> get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
> - }
> - break;
> - case DHCP_OPT_ETHERBOOT:
> - ipxe_req = true;
> - break;
> - default:
> - break;
> - }
> - }
> + dhcp_parse_options(&in_dhcp_ptr, end,
> + &in_dhcp_msg_type, &request_ip, &ipxe_req, NULL, NULL, NULL);
>
> /* Check that the DHCP Message Type (opt 53) is present or not with
> * valid values - DHCP_MSG_DISCOVER or DHCP_MSG_REQUEST.
> @@ -2245,6 +2692,7 @@ pinctrl_handle_put_dhcp_opts(
> dhcp_data->yiaddr = 0;
> }
>
> + ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE);
> dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32));
>
> uint16_t out_dhcp_opts_size = 12;
> @@ -3215,6 +3663,16 @@ process_packet_in(struct rconn *swconn, const struct
> ofp_header *msg)
> ovs_mutex_unlock(&pinctrl_mutex);
> break;
>
> + case ACTION_OPCODE_DHCP_RELAY_REQ_CHK:
> + pinctrl_handle_dhcp_relay_req_chk(swconn, &packet, &pin,
> + &userdata, &continuation);
> + break;
> +
> + case ACTION_OPCODE_DHCP_RELAY_RESP_CHK:
> + pinctrl_handle_dhcp_relay_resp_chk(swconn, &packet, &pin,
> + &userdata, &continuation);
> + break;
> +
> case ACTION_OPCODE_PUT_DHCP_OPTS:
> pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers,
> &userdata, &continuation);
> 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);
> }
> diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> index ad514a922..5eeb7c1a0 100644
> --- a/lib/ovn-l7.h
> +++ b/lib/ovn-l7.h
> @@ -68,7 +68,9 @@ struct gen_opts_map {
> * OVN_DHCP_OPT_CODE_<opt_name>.
> */
> #define OVN_DHCP_OPT_CODE_NETMASK 1
> +#define OVN_DHCP_OPT_CODE_ROUTER_IP 3
> #define OVN_DHCP_OPT_CODE_LEASE_TIME 51
> +#define OVN_DHCP_OPT_CODE_SERVER_ID 54
> #define OVN_DHCP_OPT_CODE_T1 58
> #define OVN_DHCP_OPT_CODE_T2 59
>
> diff --git a/northd/northd.c b/northd/northd.c
> index 2c3560ce2..1f7a62982 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -91,7 +91,6 @@ static bool use_ct_inv_match = true;
> static bool default_acl_drop;
>
> #define MAX_OVN_TAGS 4096
> -
>
> /* Due to various hard-coded priorities need to implement ACLs, the
> * northbound database supports a smaller range of ACL priorities than
> @@ -153,6 +152,8 @@ static bool default_acl_drop;
> #define REGBIT_LOOKUP_NEIGHBOR_IP_RESULT "reg9[3]"
> #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]"
> #define REGBIT_KNOWN_LB_SESSION "reg9[6]"
> +#define REGBIT_DHCP_RELAY_REQ_CHK "reg9[7]"
> +#define REGBIT_DHCP_RELAY_RESP_CHK "reg9[8]"
>
> /* Register to store the eth address associated to a router port for packets
> * received in S_ROUTER_IN_ADMISSION.
> @@ -168,6 +169,7 @@ static bool default_acl_drop;
> #define REG_NEXT_HOP_IPV6 "xxreg0"
> #define REG_SRC_IPV4 "reg1"
> #define REG_SRC_IPV6 "xxreg1"
> +#define REG_DHCP_RELAY_DIP_IPV4 "reg2"
> #define REG_ROUTE_TABLE_ID "reg7"
>
> /* Register used to store backend ipv6 address
> @@ -232,7 +234,7 @@ static bool default_acl_drop;
> * | R1 | SRC_IPV4 for ARP-REQ | 0 | | R |
> |
> * | | (>= IP_INPUT) | | | E |
> NEXT_HOP_IPV6 (>= DEFRAG ) |
> * +-----+---------------------------+---+-----------------+ G |
> |
> - * | R2 | UNUSED | X | | 0 |
> |
> + * | R2 REG_DHCP_RELAY_DIP_IPV4 | X | | 0 |
> |
> * | | | R | | |
> |
> * +-----+---------------------------+ E | UNUSED | |
> |
> * | R3 | UNUSED | G | | |
> |
> @@ -259,7 +261,9 @@ static bool default_acl_drop;
> * | | EGRESS_LOOPBACK/ | G | UNUSED |
> * | R9 | PKT_LARGER/ | 4 | |
> * | | LOOKUP_NEIGHBOR_RESULT/ | | |
> - * | | SKIP_LOOKUP_NEIGHBOR} | | |
> + * | | SKIP_LOOKUP_NEIGHBOR/ | | |
> + * | |REGBIT_DHCP_RELAY_REQ_CHK/ | | |
> + * | |REGBIT_DHCP_RELAY_RESP_CHK}| | |
> * | | | | |
> * | | REG_ORIG_TP_DPORT_ROUTER | | |
> * | | | | |
> @@ -8555,6 +8559,85 @@ build_dhcpv6_options_flows(struct ovn_port *op,
> ds_destroy(&match);
> }
>
> +static const char *
> +ls_dhcp_relay_port(const struct ovn_datapath *od)
> +{
> + return smap_get(&od->nbs->other_config, "dhcp_relay_port");
> +}
> +
> +static void
> +build_lswitch_dhcp_relay_flows(struct ovn_port *op,
> + const struct hmap *ls_ports,
> + struct lflow_table *lflows)
> +{
> + if (op->nbrp || !op->nbsp) {
> + return;
> + }
> +
> + /* consider only ports attached to VMs */
> + if (strcmp(op->nbsp->type, "")) {
> + return;
> + }
> +
> + if (!op->od || !op->od->n_router_ports ||
> + !op->od->nbs) {
> + return;
> + }
> +
> + /* configure dhcp relay flows only when peer router has
> + * relay config enabled */
> + const char *dhcp_relay_port = ls_dhcp_relay_port(op->od);
> + if (!dhcp_relay_port) {
> + return;
> + }
> +
> + struct ds match = DS_EMPTY_INITIALIZER;
> + struct ds action = DS_EMPTY_INITIALIZER;
> + struct ovn_port *sp = ovn_port_find(ls_ports, dhcp_relay_port);
> +
> + if (!sp || !sp->nbsp || !sp->peer) {
> + return;
> + }
> +
> + struct ovn_port *rp = sp->peer;
> + if (!rp || !rp->nbrp || !rp->nbrp->dhcp_relay || rp->peer != sp) {
> + return;
> + }
> +
> + char *server_ip_str = NULL;
> + uint16_t port;
> + int addr_family;
> + struct in6_addr server_ip;
> + struct nbrec_dhcp_relay *dhcp_relay = rp->nbrp->dhcp_relay;
> +
> + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str,
> + &server_ip, &port, &addr_family)) {
> + return;
> + }
> +
> + if (server_ip_str == NULL) {
> + return;
> + }
> +
> + ds_put_format(
> + &match, "inport == %s && eth.src == %s && "
> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
> + "udp.src == 68 && udp.dst == 67",
> + op->json_key, op->lsp_addrs[0].ea_s);
> + ds_put_format(&action,
> + "eth.dst=%s;outport=%s;next;/* DHCP_RELAY_REQ */",
> + rp->lrp_networks.ea_s,sp->json_key);
> + ovn_lflow_add_with_hint__(lflows, op->od,
> + S_SWITCH_IN_L2_LKUP, 100,
> + ds_cstr(&match),
> + ds_cstr(&action),
> + op->key,
> + NULL,
> + &op->nbsp->header_,
> + op->lflow_ref);
> + free(server_ip_str);
> +}
> +
> static void
> build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
> const struct ovn_port *port,
> @@ -9156,6 +9239,13 @@ build_lswitch_dhcp_options_and_response(struct
> ovn_port *op,
> return;
> }
>
> + if (op->od && op->od->nbs
> + && ls_dhcp_relay_port(op->od)) {
> + /* Don't add the DHCP server flows if DHCP Relay is enabled on the
> + * logical switch. */
> + return;
> + }
> +
> bool is_external = lsp_is_external(op->nbsp);
> if (is_external && (!op->od->n_localnet_ports ||
> !op->nbsp->ha_chassis_group)) {
> @@ -13636,6 +13726,165 @@ build_dhcpv6_reply_flows_for_lrouter_port(
> }
> }
>
> +static void
> +build_dhcp_relay_flows_for_lrouter_port(
> + struct ovn_port *op, struct lflow_table *lflows,
> + struct ds *match, struct lflow_ref *lflow_ref)
> +{
> + if (!op->nbrp || !op->nbrp->dhcp_relay) {
> + return;
> +
> + }
> +
> + /* configure dhcp relay flows only when peer switch has
> + * relay config enabled */
> + struct ovn_port *sp = op->peer;
> + if (!sp || !sp->nbsp || sp->peer != op ||
> + !sp->od || !ls_dhcp_relay_port(sp->od)) {
> + return;
> + }
> +
> + struct nbrec_dhcp_relay *dhcp_relay = op->nbrp->dhcp_relay;
> + if (!dhcp_relay->servers) {
> + return;
> + }
> +
> + int addr_family;
> + /* currently not supporting custom port,
> + * dhcp server port is always set to 67 when installing flows */
> + uint16_t port;
> + char *server_ip_str = NULL;
> + struct in6_addr server_ip;
> +
> + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str,
> + &server_ip, &port, &addr_family)) {
> + return;
> + }
> +
> + if (server_ip_str == NULL) {
> + return;
> + }
> +
> + struct ds dhcp_action = DS_EMPTY_INITIALIZER;
> + ds_clear(match);
> + ds_put_format(
> + match, "inport == %s && "
> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
> + "udp.src == 68 && udp.dst == 67",
> + op->json_key);
> + ds_put_format(&dhcp_action,
> + REGBIT_DHCP_RELAY_REQ_CHK
> + " = dhcp_relay_req_chk(%s, %s);"
> + "next; /* DHCP_RELAY_REQ */",
> + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
> +
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "inport == %s && "
> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
> + "udp.src == 68 && udp.dst == 67 && "
> + REGBIT_DHCP_RELAY_REQ_CHK,
> + op->json_key);
> + ds_put_format(&dhcp_action,
> + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_RELAY_REQ
> */",
> + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
> +
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 100,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "inport == %s && "
> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
> + "udp.src == 68 && udp.dst == 67 && "
> + REGBIT_DHCP_RELAY_REQ_CHK" == 0",
> + op->json_key);
> + ds_put_format(&dhcp_action,
> + "drop; /* DHCP_RELAY_REQ */");
> +
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 1,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "ip4.src == %s && ip4.dst == %s && "
> + "udp.src == 67 && udp.dst == 67",
> + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
> + ds_put_format(&dhcp_action, "next;/* DHCP_RELAY_RESP */");
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "ip4.src == %s && ip4.dst == %s && "
> + "udp.src == 67 && udp.dst == 67",
> + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
> + ds_put_format(&dhcp_action,
> + REG_DHCP_RELAY_DIP_IPV4" = ip4.dst;"
> + REGBIT_DHCP_RELAY_RESP_CHK
> + " = dhcp_relay_resp_chk(%s, %s);next;/* DHCP_RELAY_RESP */",
> + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str);
> +
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK,
> + 100,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "ip4.src == %s && "
> + REG_DHCP_RELAY_DIP_IPV4" == %s && "
> + "udp.src == 67 && udp.dst == 67 && "
> + REGBIT_DHCP_RELAY_RESP_CHK,
> + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
> + ds_put_format(&dhcp_action,
> + "ip4.src=%s;udp.dst=68;"
> + "outport=%s;output; /* DHCP_RELAY_RESP */",
> + op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key);
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP,
> + 100,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> +
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + ds_put_format(
> + match, "ip4.src == %s && "
> + REG_DHCP_RELAY_DIP_IPV4" == %s && "
> + "udp.src == 67 && udp.dst == 67 && "
> + REGBIT_DHCP_RELAY_RESP_CHK" == 0",
> + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s);
> + ds_put_format(&dhcp_action,
> + "drop; /* DHCP_RELAY_RESP */");
> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP,
> + 1,
> + ds_cstr(match), ds_cstr(&dhcp_action),
> + &op->nbrp->header_, lflow_ref);
> + ds_clear(match);
> + ds_clear(&dhcp_action);
> +
> + free(server_ip_str);
> +}
> +
> static void
> build_ipv6_input_flows_for_lrouter_port(
> struct ovn_port *op, struct lflow_table *lflows,
> @@ -14906,6 +15155,13 @@ static void build_lr_nat_defrag_and_lb_default_flows(
> lflow_ref);
> ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;",
> lflow_ref);
> + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_REQ, 0, "1",
> + "next;", lflow_ref);
> + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, 0, "1",
> + "next;", lflow_ref);
> + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP, 0, "1",
> + "next;", lflow_ref);
> +
>
> /* Send the IPv6 NS packets to next table. When ovn-controller
> * generates IPv6 NS (for the action - nd_ns{}), the injected
> @@ -15670,6 +15926,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
> ovn_port *op,
> build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions);
> build_lswitch_ip_unicast_lookup(op, lflows, actions,
> match);
> + build_lswitch_dhcp_relay_flows(op, ls_ports, lflows);
>
> /* Build Logical Router Flows. */
> build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions);
> @@ -15699,6 +15956,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct
> ovn_port *op,
> op->lflow_ref);
> build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> op->lflow_ref);
> + build_dhcp_relay_flows_for_lrouter_port(op, lsi->lflows, &lsi->match,
> + op->lflow_ref);
> build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows,
> &lsi->match, &lsi->actions,
> lsi->meter_groups,
> diff --git a/northd/northd.h b/northd/northd.h
> index 3f1cd8341..0a349c8f7 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -437,24 +437,29 @@ enum ovn_stage {
> PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor")
> \
> PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \
> PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \
> - PIPELINE_STAGE(ROUTER, IN, UNSNAT, 4, "lr_in_unsnat") \
> - PIPELINE_STAGE(ROUTER, IN, DEFRAG, 5, "lr_in_defrag") \
> - PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 6, "lr_in_lb_aff_check") \
> - PIPELINE_STAGE(ROUTER, IN, DNAT, 7, "lr_in_dnat") \
> - PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 8, "lr_in_lb_aff_learn") \
> - PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 9, "lr_in_ecmp_stateful") \
> - PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 10, "lr_in_nd_ra_options") \
> - PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 11, "lr_in_nd_ra_response")
> \
> - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 12, "lr_in_ip_routing_pre")
> \
> - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 13, "lr_in_ip_routing")
> \
> - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 14,
> "lr_in_ip_routing_ecmp") \
> - PIPELINE_STAGE(ROUTER, IN, POLICY, 15, "lr_in_policy")
> \
> - PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 16, "lr_in_policy_ecmp")
> \
> - PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 17, "lr_in_arp_resolve")
> \
> - PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 18, "lr_in_chk_pkt_len")
> \
> - PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 19, "lr_in_larger_pkts")
> \
> - PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 20, "lr_in_gw_redirect")
> \
> - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 21, "lr_in_arp_request")
> \
> + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_REQ, 4, "lr_in_dhcp_relay_req") \
> + PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \
> + PIPELINE_STAGE(ROUTER, IN, DEFRAG, 6, "lr_in_defrag") \
> + PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 7, "lr_in_lb_aff_check") \
> + PIPELINE_STAGE(ROUTER, IN, DNAT, 8, "lr_in_dnat") \
> + PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 9, "lr_in_lb_aff_learn") \
> + PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 10, "lr_in_ecmp_stateful") \
> + PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 11, "lr_in_nd_ra_options") \
> + PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 12, "lr_in_nd_ra_response")
> \
> + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 13, "lr_in_ip_routing_pre")
> \
> + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 14, "lr_in_ip_routing")
> \
> + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 15,
> "lr_in_ip_routing_ecmp") \
> + PIPELINE_STAGE(ROUTER, IN, POLICY, 16, "lr_in_policy")
> \
> + PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 17, "lr_in_policy_ecmp")
> \
> + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 18,
> \
> + "lr_in_dhcp_relay_resp_chk")
> \
> + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 19,
> \
> + "lr_in_dhcp_relay_resp")
> \
> + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 20, "lr_in_arp_resolve")
> \
> + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 21, "lr_in_chk_pkt_len")
> \
> + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 22, "lr_in_larger_pkts")
> \
> + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 23, "lr_in_gw_redirect")
> \
> + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 24, "lr_in_arp_request")
> \
> \
> /* Logical router egress stages. */ \
> PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0,
> \
> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> index a9c5b7af5..a616dbd6b 100644
> --- a/ovn-nb.ovsschema
> +++ b/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Northbound",
> "version": "7.3.0",
> - "cksum": "3546526738 34483",
> + "cksum": "3858903371 35372",
> "tables": {
> "NB_Global": {
> "columns": {
> @@ -436,6 +436,11 @@
> "ipv6_prefix": {"type": {"key": "string",
> "min": 0,
> "max": "unlimited"}},
> + "dhcp_relay": {"type": {"key": {"type": "uuid",
> + "refTable": "DHCP_Relay",
> + "refType": "strong"},
> + "min": 0,
> + "max": 1}},
> "external_ids": {
> "type": {"key": "string", "value": "string",
> "min": 0, "max": "unlimited"}},
> @@ -534,6 +539,18 @@
> "type": {"key": "string", "value": "string",
> "min": 0, "max": "unlimited"}}},
> "isRoot": true},
> + "DHCP_Relay": {
> + "columns": {
> + "name": {"type": "string"},
> + "servers": {"type": {"key": "string",
> + "min": 0,
> + "max": 1}},
> + "options": {"type": {"key": "string", "value": "string",
> + "min": 0, "max": "unlimited"}},
> + "external_ids": {
> + "type": {"key": "string", "value": "string",
> + "min": 0, "max": "unlimited"}}},
> + "isRoot": true},
> "Connection": {
> "columns": {
> "target": {"type": "string"},
> diff --git a/ovn-nb.xml b/ovn-nb.xml
> index e0b983ed6..6f0210325 100644
> --- a/ovn-nb.xml
> +++ b/ovn-nb.xml
> @@ -703,6 +703,13 @@
> </ul>
> </column>
>
> + <column name="other_config" key="dhcp_relay_port">
> + If set to the name of logical switch port of type <code>router</code>
> + then, DHCP Relay is enabled for this logical switch provided the
> + corresponding <ref table="Logical_Router_Port"/> has DHCP Relay
> + configured.
> + </column>
> +
> <column name="other_config" key="mac_only" type='{"type": "boolean"}'>
> Value used to request to assign L2 address only if neither subnet
> nor ipv6_prefix are specified
> @@ -3059,6 +3066,11 @@ or
> port has all ingress and egress traffic dropped.
> </column>
>
> + <column name="dhcp_relay">
> + This column is used to enabled DHCP Relay. Please refer
> + to <ref table="DHCP_Relay"/> table.
> + </column>
> +
> <group title="Distributed Gateway Ports">
> <p>
> Gateways, as documented under <code>Gateways</code> in the OVN
> @@ -4372,6 +4384,33 @@ or
> </group>
> </table>
>
> + <table name="DHCP_Relay" title="DHCP Relay">
> + <p>
> + OVN implements native DHCPv4 relay support which caters to the common
> + use case of relaying the DHCP requests to external DHCP server.
> + </p>
> + <column name="name">
> + <p>
> + A name for the DHCP Relay.
> + </p>
> + </column>
> + <column name="servers">
> + <p>
> + The DHCPv4 server IP address.
> + </p>
> + </column>
> + <column name="options">
> + <p>
> + Future purpose.
> + </p>
> + </column>
> + <group title="Common Columns">
> + <column name="external_ids">
> + See <em>External IDs</em> at the beginning of this document.
> + </column>
> + </group>
> + </table>
> +
> <table name="Connection" title="OVSDB client connections.">
> <p>
> Configuration for a database connection to an Open vSwitch database
> diff --git a/tests/atlocal.in b/tests/atlocal.in
> index 63d891b89..32d1c374e 100644
> --- a/tests/atlocal.in
> +++ b/tests/atlocal.in
> @@ -187,6 +187,9 @@ fi
> # Set HAVE_DHCPD
> find_command dhcpd
>
> +# Set HAVE_DHCLIENT
> +find_command dhclient
> +
> # Set HAVE_BFDD_BEACON
> find_command bfdd-beacon
>
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index c189dcccc..c8d551f2d 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -12138,6 +12138,44 @@ check_row_count nb:QoS 0
> AT_CLEANUP
> ])
>
> +OVN_FOR_EACH_NORTHD_NO_HV([
> +AT_SETUP([check DHCP RELAY])
> +ovn_start NORTHD_TYPE
> +
> +check ovn-nbctl ls-add ls0
> +check ovn-nbctl lsp-add ls0 ls0-port1
> +check ovn-nbctl lsp-set-addresses ls0-port1 02:00:00:00:00:10
> +check ovn-nbctl lr-add lr0
> +check ovn-nbctl lrp-add lr0 lrp1 02:00:00:00:00:01 192.168.1.1/24
> +check ovn-nbctl lsp-add ls0 lrp1-attachment
> +check ovn-nbctl lsp-set-type lrp1-attachment router
> +check ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02
> +check ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1
> +check ovn-nbctl lrp-add lr0 lrp-ext 02:00:00:00:00:02 192.168.2.1/24
> +
> +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1)
> +check ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay
> +check ovn-nbctl set Logical_Switch ls0
> other_config:dhcp_relay_port=lrp1-attachment
> +
> +check ovn-nbctl --wait=sb sync
> +
> +ovn-sbctl lflow-list > lflows
> +AT_CAPTURE_FILE([lflows])
> +
> +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed 's/table=../table=??/'], [0],
> [dnl
> + table=??(lr_in_ip_input ), priority=110 , match=(inport == "lrp1" &&
> ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst
> == 67), action=(reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);next;
> /* DHCP_RELAY_REQ */)
> + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src ==
> 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67),
> action=(next;/* DHCP_RELAY_RESP */)
> + table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" &&
> ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst
> == 67 && reg9[[7]]),
> action=(ip4.src=192.168.1.1;ip4.dst=172.16.1.1;udp.src=67;next; /*
> DHCP_RELAY_REQ */)
> + table=??(lr_in_dhcp_relay_req), priority=1 , match=(inport == "lrp1" &&
> ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst
> == 67 && reg9[[7]] == 0), action=(drop; /* DHCP_RELAY_REQ */)
> + table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src ==
> 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67),
> action=(reg2 = ip4.dst;reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1,
> 172.16.1.1);next;/* DHCP_RELAY_RESP */)
> + table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src ==
> 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 &&
> reg9[[8]]), action=(ip4.src=192.168.1.1;udp.dst=68;outport="lrp1";output; /*
> DHCP_RELAY_RESP */)
> + table=??(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src ==
> 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 &&
> reg9[[8]] == 0), action=(drop; /* DHCP_RELAY_RESP */)
> + table=??(ls_in_l2_lkup ), priority=100 , match=(inport ==
> "ls0-port1" && eth.src == 02:00:00:00:00:10 && ip4.src == 0.0.0.0 && ip4.dst
> == 255.255.255.255 && udp.src == 68 && udp.dst == 67),
> action=(eth.dst=02:00:00:00:00:01;outport="lrp1-attachment";next;/*
> DHCP_RELAY_REQ */)
> +])
> +
> +AT_CLEANUP
> +])
> +
> AT_SETUP([NB_Global and SB_Global incremental processing])
>
> ovn_start
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 902dd3793..109e19550 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -1661,6 +1661,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.40.00.00.00)
> @@ -21996,7 +22030,7 @@ eth_dst=00000000ff01
> ip_src=$(ip_to_hex 10 0 0 10)
> ip_dst=$(ip_to_hex 172 168 0 101)
> send_icmp_packet 1 1 $eth_src $eth_dst $ip_src $ip_dst c4c9
> 0000000000000000000000
> -AT_CHECK_UNQUOTED([as hv1 ovs-ofctl dump-flows br-int metadata=0x$lr0_dp_key
> | awk '/table=28, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl
> +AT_CHECK_UNQUOTED([as hv1 ovs-ofctl dump-flows br-int metadata=0x$lr0_dp_key
> | awk '/table=31, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl
>
> priority=80,ip,reg15=0x$lr0_public_dp_key,metadata=0x$lr0_dp_key,nw_src=10.0.0.10
> actions=drop
> ])
>
> @@ -28221,7 +28255,7 @@ ovn-sbctl dump-flows > sbflows
> AT_CAPTURE_FILE([sbflows])
> AT_CAPTURE_FILE([offlows])
> OVS_WAIT_UNTIL([
> - as hv1 ovs-ofctl dump-flows br-int table=23 > offlows
> + as hv1 ovs-ofctl dump-flows br-int table=24 > offlows
> test $(grep -c "load:0x64->NXM_NX_PKT_MARK" offlows) = 1 && \
> test $(grep -c "load:0x3->NXM_NX_PKT_MARK" offlows) = 1 && \
> test $(grep -c "load:0x4->NXM_NX_PKT_MARK" offlows) = 1 && \
> @@ -28319,12 +28353,12 @@ send_ipv4_pkt hv1 hv1-vif1 505400000003
> 00000000ff01 \
> c3ad 83dc
>
> OVS_WAIT_UNTIL([
> - test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=23 | \
> + test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=24 | \
> grep "load:0x2->NXM_NX_PKT_MARK" -c)
> ])
>
> AT_CHECK([
> - test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=23 | \
> + test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=24 | \
> grep "load:0x64->NXM_NX_PKT_MARK" -c)
> ])
>
> @@ -29017,23 +29051,23 @@ check ovn-nbctl --wait=hv sync
>
> # Ensure ECMP symmetric reply flows are not present on any hypervisor.
> AT_CHECK([
> - test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=17 | \
> + test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=18 | \
> grep "priority=100" | \
> grep
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
> -c)
> ])
> AT_CHECK([
> - test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=25 | \
> + test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=26 | \
> grep "priority=200" | \
> grep
> "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
> ])
>
> AT_CHECK([
> - test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=17 | \
> + test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=18 | \
> grep "priority=100" | \
> grep
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
> -c)
> ])
> AT_CHECK([
> - test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=25 | \
> + test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=26 | \
> grep "priority=200" | \
> grep
> "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
> ])
> @@ -29051,11 +29085,11 @@ AT_CAPTURE_FILE([hv2flows])
>
> AT_CHECK([
> for hv in 1 2; do
> - grep table=17 hv${hv}flows | \
> + grep table=18 hv${hv}flows | \
> grep "priority=100" | \
> grep -c
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]]))"
>
> - grep table=25 hv${hv}flows | \
> + grep table=28 hv${hv}flows | \
> grep "priority=200" | \
> grep -c
> "move:NXM_NX_CT_LABEL\\[[\\]]->NXM_NX_XXREG1\\[[\\]],move:NXM_NX_XXREG1\\[[32..79\\]]->NXM_OF_ETH_DST"
> done; :], [0], [dnl
> @@ -29143,23 +29177,23 @@ check ovn-nbctl --wait=hv sync
>
> # Ensure ECMP symmetric reply flows are not present on any hypervisor.
> AT_CHECK([
> - test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=17 | \
> + test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=18 | \
> grep "priority=100" | \
> grep
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
> -c)
> ])
> AT_CHECK([
> - test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=25 | \
> + test 0 -eq $(as hv1 ovs-ofctl dump-flows br-int table=28 | \
> grep "priority=200" | \
> grep
> "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
> ])
>
> AT_CHECK([
> - test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=17 | \
> + test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=18 | \
> grep "priority=100" | \
> grep
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]]))"
> -c)
> ])
> AT_CHECK([
> - test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=25 | \
> + test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=28 | \
> grep "priority=200" | \
> grep "actions=move:NXM_NX_CT_LABEL\\[[\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
> ])
> @@ -29176,11 +29210,11 @@ AT_CAPTURE_FILE([hv2flows])
>
> AT_CHECK([
> for hv in 1 2; do
> - grep table=17 hv${hv}flows | \
> + grep table=18 hv${hv}flows | \
> grep "priority=100" | \
> grep -c
> "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],.*exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_MARK\\[[16..31\\]]))"
>
> - grep table=25 hv${hv}flows | \
> + grep table=28 hv${hv}flows | \
> grep "priority=200" | \
> grep -c
> "move:NXM_NX_CT_LABEL\\[[\\]]->NXM_NX_XXREG1\\[[\\]],move:NXM_NX_XXREG1\\[[32..79\\]]->NXM_OF_ETH_DST"
> done; :], [0], [dnl
> @@ -29677,7 +29711,7 @@ if test X"$1" = X"DGP"; then
> else
> prio=2
> fi
> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=25,
> n_packets=1,.*
> priority=$prio,ip,$inport.*$outport.*metadata=0x${sw_key},nw_dst=10.0.1.1
> actions=drop" -c], [0], [dnl
> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=28,
> n_packets=1,.*
> priority=$prio,ip,$inport.*$outport.*metadata=0x${sw_key},nw_dst=10.0.1.1
> actions=drop" -c], [0], [dnl
> 1
> ])
>
> @@ -29696,13 +29730,13 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep
> "actions=controller" | grep
>
> if test X"$1" = X"DGP"; then
> # The packet dst should be resolved once for E/W centralized NAT purpose.
> - AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=25,
> n_packets=1,.* priority=100,reg0=0xa000101,reg15=.*metadata=0x${sw_key}
> actions=mod_dl_dst:00:00:00:00:01:01,resubmit" -c], [0], [dnl
> + AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=28,
> n_packets=1,.* priority=100,reg0=0xa000101,reg15=.*metadata=0x${sw_key}
> actions=mod_dl_dst:00:00:00:00:01:01,resubmit" -c], [0], [dnl
> 1
> ])
> fi
>
> # The packet should've been finally dropped in the lr_in_arp_resolve stage.
> -AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=25,
> n_packets=2,.*
> priority=$prio,ip,$inport.*$outport.*metadata=0x${sw_key},nw_dst=10.0.1.1
> actions=drop" -c], [0], [dnl
> +AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep -E "table=28,
> n_packets=2,.*
> priority=$prio,ip,$inport.*$outport.*metadata=0x${sw_key},nw_dst=10.0.1.1
> actions=drop" -c], [0], [dnl
> 1
> ])
> OVN_CLEANUP([hv1])
> @@ -34645,7 +34679,7 @@ check ovn-nbctl set nb_global .
> options:use_common_zone="true"
> check ovn-nbctl --wait=hv sync
> # Use constants so that if tables or registers change, this test can
> # be updated easily.
> -DNAT_TABLE=15
> +DNAT_TABLE=16
> SNAT_TABLE=45
> DNAT_ZONE_REG="NXM_NX_REG11[[0..15]]"
> SNAT_ZONE_REG="NXM_NX_REG12[[0..15]]"
> @@ -38088,3 +38122,222 @@ OVS_WAIT_UNTIL([test 1 = $(as hv ovs-ofctl
> dump-flows br-int | grep -E "pkt_mark
> OVN_CLEANUP([hv])
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([DHCP RELAY])
> +ovn_start
> +net_add n1
> +
> +AT_CHECK([ovn-nbctl ls-add ls0])
> +AT_CHECK([ovn-nbctl lsp-add ls0 vif0])
> +AT_CHECK([ovn-nbctl lsp-set-addresses vif0 "50:54:00:00:00:10"])
> +AT_CHECK([ovn-nbctl lsp-add ls0 lrp1-attachment])
> +AT_CHECK([ovn-nbctl lsp-set-type lrp1-attachment router])
> +AT_CHECK([ovn-nbctl lsp-set-addresses lrp1-attachment 50:54:00:00:00:01])
> +AT_CHECK([ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1])
> +
> +AT_CHECK([ovn-nbctl lr-add lr0])
> +AT_CHECK([ovn-nbctl lrp-add lr0 lrp1 50:54:00:00:00:01 192.168.1.1/24])
> +AT_CHECK([ovn-nbctl lrp-add lr0 lrp2 50:54:00:00:00:02 172.16.1.254/24])
> +
> +AT_CHECK([ovn-nbctl ls-add ls-ext])
> +AT_CHECK([ovn-nbctl lsp-add ls-ext lrp2-attachment])
> +AT_CHECK([ovn-nbctl lsp-set-type lrp2-attachment router])
> +AT_CHECK([ovn-nbctl lsp-set-addresses lrp2-attachment 50:54:00:00:00:02])
> +AT_CHECK([ovn-nbctl lsp-set-options lrp2-attachment router-port=lrp2])
> +AT_CHECK([ovn-nbctl lsp-add ls-ext ln_port])
> +AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
> +AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
> +AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
> +
> +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1)
> +AT_CHECK([ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay])
> +AT_CHECK([ovn-nbctl set Logical_Switch ls0
> other_config:dhcp_relay_port=lrp1-attachment])
> +
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovs-vsctl -- add-port br-int vif0 -- \
> + set interface vif0 external-ids:iface-id=vif0 \
> + options:tx_pcap=hv1/vif0-tx.pcap \
> + options:rxq_pcap=hv1/vif0-rx.pcap \
> + ofport-request=1
> +ovs-vsctl -- add-port br-phys ext0 -- \
> + set interface ext0 \
> + options:tx_pcap=hv1/ext0-tx.pcap \
> + options:rxq_pcap=hv1/ext0-rx.pcap \
> + ofport-request=2
> +
> +ovs-vsctl set open . external_ids:ovn-bridge-mappings=physnet1:br-phys
> +
> +wait_for_ports_up
> +AT_CHECK([ovn-nbctl --wait=hv sync])
> +
> +send_dhcp_packet() {
> + src_mac=${1}
> + src_ip=${2}
> + dst_mac=${3}
> + dst_ip=${4}
> + op_code=${5}
> + msg_type=${6}
> + yiaddr=$7
> + giaddr=${8}
> + sid=${9}
> + iface=${10}
> + #echo "ARGS: ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10}"
> + echo "ARGS: $@"
> + if [[ $op_code == "01" ]]; then
> + ip_len=0111
> + udp_len=00fd
> + src_port=0044
> + else
> + ip_len=011d
> + udp_len=0109
> + src_port=0043
> + fi
> + flags=0000
> +
> + local
> pkt=${dst_mac}${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip}
> + # udp header and dhcp header
> + pkt=${pkt}${src_port}0043${udp_len}0000
> +
> pkt=${pkt}${op_code}0106006359aa760000${flags}00000000${yiaddr}00000000${giaddr}${src_mac}
> + # client hardware padding
> + pkt=${pkt}00000000000000000000
> + # server hostname
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> + # boot file name
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> +
> pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000
> + # dhcp magic cookie
> + pkt=${pkt}63825363
> + # dhcp message type
> + pkt=${pkt}3501${msg_type}
> + # dhcp server identifier and subnet mask options
> + if [[ $op_code == "02" ]]; then
> + pkt=${pkt}3604${sid}
> + pkt=${pkt}0104ffffff00
> + fi
> + # dhcp pad option
> + pkt=${pkt}00
> + # dhcp end option
> + pkt=${pkt}ff
> +
> + tcpdump_hex "-- sending DHCP pkt on hv1-$iface" $pkt
> +
> + ovs-appctl netdev-dummy/receive $iface $pkt
> +}
> +
> +
> +# Check that there is commit_fdb_local_fdb() flow added by ovn-northd for
> vif0 and localnet
> +ovn-sbctl dump-flows > lflows
> +AT_CAPTURE_FILE([lflows])
> +
> +# ====================================================
> +# Send DHCP valid discovery
> +src_mac="505400000010"
> +src_ip=`ip_to_hex 0.0.0.0`
> +dst_mac="ffffffffffff"
> +dst_ip=`ip_to_hex 255.255.255.255`
> +yiaddr=`ip_to_hex 0.0.0.0`
> +giaddr=`ip_to_hex 0.0.0.0`
> +sid=$src_ip
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr
> $sid vif0
> +
> +ovs-ofctl dump-flows br-int table=12 > pflows1_dhcp_relay_req
> +AT_CAPTURE_FILE([pflows1_dhcp_relay_req])
> +
> +AT_CHECK([cat pflows1_dhcp_relay_req | grep -v NXST | grep 255.255.255.255 |
> grep resubmit |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=1
> +])
> +
> +# ====================================================
> +# Send DHCP discovery with giaddr set
> +giaddr=`ip_to_hex 192.168.1.1`
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr
> $sid vif0
> +
> +ovs-ofctl dump-flows br-int table=12 > pflows2_dhcp_relay_req
> +AT_CAPTURE_FILE([pflows2_dhcp_relay_req])
> +
> +AT_CHECK([cat pflows2_dhcp_relay_req | grep -v NXST | grep 255.255.255.255 |
> grep drop |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=1
> +])
> +
> +# ====================================================
> +# Send DHCP valid offer
> +src_mac="50540000001f"
> +src_ip=`ip_to_hex 172.16.1.1`
> +dst_mac="505400000002"
> +dst_ip=`ip_to_hex 192.168.1.1`
> +yiaddr=`ip_to_hex 192.168.1.10`
> +giaddr=`ip_to_hex 192.168.1.1`
> +sid=$src_ip
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr
> $sid ext0
> +
> +ovs-ofctl dump-flows br-int table=27 > pflows1_dhcp_relay_resp
> +AT_CAPTURE_FILE([pflows1_dhcp_relay_resp])
> +
> +AT_CHECK([cat pflows1_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 |
> grep resubmit |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=1
> +])
> +# ====================================================
> +# Send DHCP offer with incorrect giaddr
> +giaddr=`ip_to_hex 192.168.1.10`
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr
> $sid ext0
> +
> +ovs-ofctl dump-flows br-int table=27 > pflows2_dhcp_relay_resp
> +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp])
> +
> +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 |
> grep drop |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=1
> +])
> +
> +giaddr=`ip_to_hex 192.168.1.1`
> +
> +# ====================================================
> +# Send DHCP offer with yiaddr outside of the subnet
> +yiaddr=`ip_to_hex 192.168.2.10`
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr
> $sid ext0
> +
> +ovs-ofctl dump-flows br-int table=27 > pflows2_dhcp_relay_resp
> +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp])
> +
> +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 |
> grep drop |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=2
> +])
> +
> +yiaddr=`ip_to_hex 192.168.1.10`
> +
> +# ====================================================
> +# Send DHCP offer with differnt server identifier
> +sid=`ip_to_hex 172.16.1.100`
> +# send packet
> +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr
> $sid ext0
> +
> +ovs-ofctl dump-flows br-int table=27 > pflows2_dhcp_relay_resp
> +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp])
> +
> +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 |
> grep drop |
> +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl
> +n_packets=3
> +])
> +
> +sid=`ip_to_hex 172.16.1.1`
> +
> +OVN_CLEANUP([hv1])
> +AT_CLEANUP
> +])
> +
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index c22c7882f..e7fa79727 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -12184,3 +12184,151 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port
> patch-.*/d
> /connection dropped.*/d"])
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([DHCP RELAY])
> +AT_SKIP_IF([test $HAVE_DHCPD = no])
> +AT_SKIP_IF([test $HAVE_DHCLIENT = no])
> +AT_SKIP_IF([test $HAVE_TCPDUMP = no])
> +ovn_start
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +ADD_BR([br-int])
> +ADD_BR([br-ext])
> +
> +ovs-ofctl add-flow br-ext action=normal
> +# Set external-ids in br-int needed for ovn-controller
> +ovs-vsctl \
> + -- set Open_vSwitch . external-ids:system-id=hv1 \
> + -- set Open_vSwitch .
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
> + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
> + -- set bridge br-int fail-mode=secure
> other-config:disable-in-band=true
> +
> +# Start ovn-controller
> +start_daemon ovn-controller
> +
> +ADD_NAMESPACES(sw01)
> +ADD_VETH(sw01, sw01, br-int, "0", "f0:00:00:01:02:03")
> +ADD_NAMESPACES(sw11)
> +ADD_VETH(sw11, sw11, br-int, "0", "f0:00:00:02:02:03")
> +ADD_NAMESPACES(server)
> +ADD_VETH(s1, server, br-ext, "172.16.1.1/24", "f0:00:00:01:02:05", \
> + "172.16.1.254")
> +
> +check ovn-nbctl lr-add R1
> +
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl ls-add sw1
> +check ovn-nbctl ls-add sw-ext
> +
> +check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
> +check ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24
> +check ovn-nbctl lrp-add R1 rp-ext 00:00:02:01:02:03 172.16.1.254/24
> +
> +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1)
> +check ovn-nbctl set Logical_Router_port rp-sw0 dhcp_relay=$dhcp_relay
> +check ovn-nbctl set Logical_Router_port rp-sw1 dhcp_relay=$dhcp_relay
> +check ovn-nbctl lrp-set-gateway-chassis rp-ext hv1
> +
> +check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
> + type=router options:router-port=rp-sw0 \
> + -- lsp-set-addresses sw0-rp router
> +check ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \
> + type=router options:router-port=rp-sw1 \
> + -- lsp-set-addresses sw1-rp router
> +
> +check ovn-nbctl set Logical_Switch sw0 other_config:dhcp_relay_port=sw0-rp
> +check ovn-nbctl set Logical_Switch sw1 other_config:dhcp_relay_port=sw1-rp
> +
> +check ovn-nbctl lsp-add sw-ext ext-rp -- set Logical_Switch_Port ext-rp \
> + type=router options:router-port=rp-ext \
> + -- lsp-set-addresses ext-rp router
> +check ovn-nbctl lsp-add sw-ext lnet \
> + -- lsp-set-addresses lnet unknown \
> + -- lsp-set-type lnet localnet \
> + -- lsp-set-options lnet network_name=phynet
> +
> +check ovn-nbctl lsp-add sw0 sw01 \
> + -- lsp-set-addresses sw01 "f0:00:00:01:02:03"
> +
> +check ovn-nbctl lsp-add sw1 sw11 \
> + -- lsp-set-addresses sw11 "f0:00:00:02:02:03"
> +
> +AT_CHECK([ovs-vsctl set Open_vSwitch .
> external-ids:ovn-bridge-mappings=phynet:br-ext])
> +
> +OVN_POPULATE_ARP
> +
> +check ovn-nbctl --wait=hv sync
> +
> +DHCP_TEST_DIR="/tmp/dhcp-test"
> +rm -rf $DHCP_TEST_DIR
> +mkdir $DHCP_TEST_DIR
> +cat > $DHCP_TEST_DIR/dhcpd.conf <<EOF
> +subnet 172.16.1.0 netmask 255.255.255.0 {
> +}
> +subnet 192.168.1.0 netmask 255.255.255.0 {
> + range 192.168.1.10 192.168.1.10;
> + option routers 192.168.1.1;
> + option broadcast-address 192.168.1.255;
> + default-lease-time 60;
> + max-lease-time 120;
> +}
> +subnet 192.168.2.0 netmask 255.255.255.0 {
> + range 192.168.2.10 192.168.2.10;
> + option routers 192.168.2.1;
> + option broadcast-address 192.168.2.255;
> + default-lease-time 60;
> + max-lease-time 120;
> +}
> +EOF
> +cat > $DHCP_TEST_DIR/dhclien.conf <<EOF
> +timeout 2
> +EOF
> +
> +touch $DHCP_TEST_DIR/dhcpd.leases
> +chown root:dhcpd $DHCP_TEST_DIR $DHCP_TEST_DIR/dhcpd.leases
> +chmod 775 $DHCP_TEST_DIR
> +chmod 664 $DHCP_TEST_DIR/dhcpd.leases
> +
> +
> +NETNS_DAEMONIZE([server], [dhcpd -4 -f -cf $DHCP_TEST_DIR/dhcpd.conf s1 >
> dhcpd.log 2>&1], [dhcpd.pid])
> +
> +NS_CHECK_EXEC([server], [tcpdump -l -nvv -i s1 udp > pkt.pcap 2>tcpdump_err
> &])
> +OVS_WAIT_UNTIL([grep "listening" tcpdump_err])
> +on_exit 'kill $(pidof tcpdump)'
> +
> +NS_CHECK_EXEC([sw01], [dhclient -1 -q -lf $DHCP_TEST_DIR/dhclient-sw01.lease
> -pf $DHCP_TEST_DIR/dhclient-sw01.pid -cf $DHCP_TEST_DIR/dhclien.conf sw01])
> +NS_CHECK_EXEC([sw11], [dhclient -1 -q -lf $DHCP_TEST_DIR/dhclient-sw11.lease
> -pf $DHCP_TEST_DIR/dhclient-sw11.pid -cf $DHCP_TEST_DIR/dhclien.conf sw11])
> +
> +OVS_WAIT_UNTIL([
> + total_pkts=$(cat pkt.pcap | wc -l)
> + test ${total_pkts} -ge 8
> +])
> +
> +on_exit 'kill `cat $DHCP_TEST_DIR/dhclient-sw01.pid` &&
> +kill `cat $DHCP_TEST_DIR/dhclient-sw11.pid` && rm -rf $DHCP_TEST_DIR'
> +
> +NS_CHECK_EXEC([sw01], [ip addr show sw01 | grep -oP
> '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl
> +192.168.1.10
> +])
> +NS_CHECK_EXEC([sw11], [ip addr show sw11 | grep -oP
> '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl
> +192.168.2.10
> +])
> +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as
> +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
> +/failed to query port patch-.*/d
> +/.*terminating with signal 15.*/d"])
> +AT_CLEANUP
> +])
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index e0f1c3ec9..903457d45 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 = 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
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev