On Fri, Sep 8, 2023 at 12:04 AM Naveen Yerramneni
<[email protected]> wrote:
>
> Hi Team,
>
> Could someone please look into this ?
>
> Thanks,
> Naveen
>
> > On 23-Aug-2023, at 8:06 AM, Naveen Yerramneni
> > <[email protected]> wrote:
> >
> > This patch contains changes to enable DHCP Proxy Agent support for overlay
> > subnets.
> >
> > NOTE:
> > -----
> > - This patch is not complete, sending this to get the initial feedback
> > about the approach taken.
> >
> > 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 proxying 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 Proxy
> > functionality in OVN.
> > 1. VM originates DHCP discovery (broadcast).
> > 2. DHCP Proxy (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.
> > 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 proxy resets the GIADDR field when forwarding the offer to the
> > client and it also replaces DHCP server ID option (54) with its IP address.
> > 5. VM/Host sends DHCP request (broadcast) packet.
> > 6. DHCP Proxy (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 also replaces the
> > DHCP server ID option (54) with DHCP server IP address.
> > 7. DHCP Server sends the ACK packet.
> > 8. DHCP Proxy resets the GIADDR field when forwarding the ACK to the
> > client and it also replaces DHCP server ID option (54) with its IP address.
> > 9. By setting DHCP server ID option (54) to its IP address, DHCP Proxy
> > completely hides the DHCP server from the end DHCP clients and all the
> > future renew/release packets come to DHCP proxy.
> >
> > OVN DHCP PROXY PACKET FLOW:
> > ----------------------------
> > To add DHCP Proxy support on OVN, we need to replicate all the behavior
> > described above using distributed logical switch and logical router.
> > At a 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. Required info is
> > extracted from the DHCP payload and stored in the in-memory hash map (key:
> > MAC + DHCP_xid) of the OVN controller.
> > 2. Response packet is first processed on RC node (which first receives the
> > packet from underlay network). RC node forwards the packet to the right
> > node by filling in the dest MAC and IP.
> > 3. Response packet is processed on the source node which originated the
> > DHCP request. Hash map lookup is done to find the associative DHCP session
> > and stateful checks are performed to validate the response.
> >
> > OVN Packet flow with DHCP Proxy is explained below.
> > 1. 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 in the DHCP payload
> > (GIADDR, SERVER ID) after doing the required checks. It stores required
> > information from the packet to do some stateful checks on
> > response packets and reinjects the packet back to the datapath provided
> > if all sanity checks are passed.
> > 5. Logical Router converts the packet to L3 unicast and forwards it to the
> > server. This packet 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 updates the destination MAC (available in DHCP header)
> > and destination IP (available in DHCP header) and reinjects the packet to
> > datapath.
> > 9. Logical router outputs the packet to the logical switch.
> > 10.Logical switch processes the packet and redirects it to the OVN
> > controller.
> > 11.OVN controller does the required checks on the packet, updates DHCP
> > payload (GIADDR, Server ID)and reinjects the packet back to the datapath if
> > all stateful checks on the packet are passed.
> > 12.Logical switch outputs the packet to VM by updating the required fields
> > in IP and UDP header.
> > 13. Similar steps are performed for further packets.
> >
> > NEW OVN ACTIONS
> > ---------------
> >
> > 1. dhcp_relay_req(<proxy-ip>, <server-ip>)
> > - This action executes on the source node on which the DHCP request
> > originated.
> > - This action proxies the DHCP request coming from client to the
> > server and stores required info about the DHCP txn in the memory. Proxy-ip
> > is used to update GIADDR in the DHCP header.
> > 2. dhcp_relay_resp_fwd()
> > - 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.
> > 3. dhcp_relay_resp(<proxy-ip>, <server-ip>)
> > - This action executes on the node where the VM (to which DHCP packet
> > is destined) is running.
> > - This action does all the stateful packet checks and updates all the
> > required info in the DHCP payload.
> > - Proxy-ip, server-ip are used to update/validate GIADDR and SERVER ID
> > in the DHCP payload.
> >
> > FLOWS
> > -----
> > Following are the new flows required to support DHCP functionality.
> > Following flows enable DHCP Proxy support for one VM port.
> > For each DHCP subnet, four flows are requried to enable the DHCP proxy.
> > For each VM port, two flows are requried to enable the DHCP proxy support
> > in the given 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-port>;next;/* DHCP_PROXY_REQ */)
> > 2. table=9 (ls_out_dhcp_proxy_resp), priority=100 , match=(inport ==
> > <lrp_port> && eth.src == <lrp_mac> && outport == <vm_port> &&ip4.src ==
> > <dhcp_server_ip> && udp.src == 67 && udp.dst == 67),
> > action=(dhcp_proxy_resp(<lrp_ip>,<dhcp_server_ip>);ip4.src=<lrp_ip>;udp.dst=68;next;/*
> > DHCP_PROXY_RESP */)
> > 3. table=3 (lr_in_ip_input ), priority=110 , match=(inport ==
> > <lrp_port> && ip4.dst == <lrp_ip> && udp.src == 68 && udp.dst == 67),
> > action=(dhcp_proxy_req(<lrp_ip>,<dhcp_server_ip>);ip4.src=<lrp_ip>;ip4.dst=<dhcp_server_ip>;udp.src=67;next;
> > /* DHCP_PROXY_REQ */)
> > 4. table=3 (lr_in_ip_input ), priority=110 , match=(inport ==
> > <lrp_port> && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src
> > == 68 && udp.dst == 67),
> > action=(dhcp_proxy_req(<lrp_ip>,<dhcp_server_ip>);ip4.src=<lrp_ip>;ip4.dst=<dhcp_server_ip>;udp.src=67;next;
> > /* DHCP_PROXY_REQ */)
> > 5. table=3 (lr_in_ip_input ), priority=110 , match=(ip4.src ==
> > <dhcp_server_ip> && ip4.dst ==<lrp_ip> && udp.src == 67 && udp.dst == 67),
> > action=(next;/* DHCP_PROXY_RESP */)
> > 6. table=17(lr_in_dhcp_proxy_resp_fwd), priority=110 , match=(ip4.src ==
> > <dhcp_server_ip> && ip4.dst == <lrp_ip> && udp.src == 67 && udp.dst == 67),
> > action=(dhcp_proxy_resp_fwd();outport=<lrp_port>;output; /* DHCP_PROXY_RESP
> > */)
> >
> > NEW PIPELINE STAGES
> > -------------------
> > Following are new stages are added for DHCP proxy feature. Some of the
> > flows are fitted into the existing pipeline tages.
> > 1. lr_in_dhcp_proxy_resp_fwd
> > - Forward teh DHCP response to the appropriate node
> > 2. ls_out_dhcp_proxy_resp
> > - Process DHCP response on the source which originated the request.
> >
> > NB SCHEMA CHANGES
> > ----------------
> > 1. New DHCP_Proxy table
> > "DHCP_Proxy": {
> > "columns": {
> > "name": {"type": "string"},
> > "servers": {"type": {"key": "string",
> > "min": 0,
> > "max": 1}},
> > "external_ids": {
> > "type": {"key": "string", "value": "string",
> > "min": 0, "max": "unlimited"}}},
> > "isRoot": true},
> > 2. New column to Logical_Router_Port table
> > "dhcp_proxy": {"type": {"key": {"type": "uuid",
> > "refTable": "DHCP_Proxy",
> > "refType": "weak"},
> > "min": 0,
> > "max": 1}},
> > 3. New column to Logical_Switch_table
> > "dhcp_proxy_port": {"type": {"key": {"type": "uuid",
> > "refTable": "Logical_Router_Port",
> > "refType": "weak"},
> > "min": 0,
> > "max": 1}}},
> > Commands to enable the feature.
> > - ovn-nbctl create DHCP_Proxy servers=<ip>
> > - ovn-nbctl set Logical_Router_port <lrp_uuid> dhcp_proxy=<dhcp_proxy_uuid>
> > - ovn-nbctl set Logical_Switch <ls_uuid> dhcp_proxy_port=<lrp_uuid>
> >
Hi Naveen,
Thanks for the RFC patch. This is not a full review. I have a few questions
1. Is there an RFC for DHCP proxy ? If so, can you please share and
link it in the patch documentation ?
If not, can you share any documentation/standard for DHCP proxy ?
2. Can you please provide a few examples on how a logical port is
created ? What address would be set for the logical port ?
And once a VM gets IP using dhcp proxy, is this IP address
stored in OVN Northbound db Logical_Switch_Port ?
How does OVN learn about this mac-ip binding for a VM and forward
the packet later for any E-W or N-S traffic ?
3. Is it possible to handle all this DHCP proxy in the logical switch
pipeline itself ? In a typical deployment where DHCP proxy is used,
Who does the DHCP proxy ? Is it the router ?
Thanks
Numan
> > POST RFC REVIEW
> > ----------------
> > 1. Address review comments/suggestions
> > 2. Address TODOs and pending changes
> > 3. Unit tests
> > 4. Complete testing
> > 5. IP Discovery support in future
> >
> > 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]>
> > CC: Abhiram Sangana <[email protected]>
> > ---
> > controller/pinctrl.c | 766 ++++++++++++++++++++++++++++++++++++++++++
> > include/ovn/actions.h | 33 ++
> > lib/actions.c | 143 ++++++++
> > lib/ovn-l7.h | 1 +
> > northd/northd.c | 218 +++++++++++-
> > ovn-nb.ovsschema | 23 +-
> > ovn-nb.xml | 27 ++
> > tests/ovn-northd.at | 32 +-
> > tests/ovn.at | 12 +-
> > utilities/ovn-trace.c | 12 +
> > 10 files changed, 1237 insertions(+), 30 deletions(-)
> >
> > diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> > index bed90fe0b..11f5839e3 100644
> > --- a/controller/pinctrl.c
> > +++ b/controller/pinctrl.c
> > @@ -377,6 +377,9 @@ static void wait_put_fdbs(struct ovsdb_idl_txn
> > *ovnsb_idl_txn);
> > static void pinctrl_handle_put_fdb(const struct flow *md,
> > const struct flow *headers)
> > OVS_REQUIRES(pinctrl_mutex);
> > +static struct dhcp_proxy_info *dhcp_proxy_get(void);
> > +static void dhcp_proxy_init(struct dhcp_proxy_info *dhcp_proxy);
> > +
> >
> > COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
> > COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
> > @@ -547,6 +550,7 @@ pinctrl_init(void)
> > init_svc_monitors();
> > bfd_monitor_init();
> > init_fdb_entries();
> > + dhcp_proxy_init(dhcp_proxy_get());
> > pinctrl.br_int_name = NULL;
> > pinctrl.mac_binding_can_timestamp = false;
> > pinctrl_handler_seq = seq_create();
> > @@ -1883,6 +1887,753 @@ is_dhcp_flags_broadcast(ovs_be16 flags)
> > return flags & htons(DHCP_BROADCAST_FLAG);
> > }
> >
> > +
> > +enum dhcp_proxy_txn_state
> > +{
> > + DHCP_PROXY_TXN_STATE_INIT=0,
> > + DHCP_PROXY_TXN_STATE_DISCOVER,
> > + DHCP_PROXY_TXN_STATE_OFFER,
> > + DHCP_PROXY_TXN_STATE_REQUEST,
> > + DHCP_PROXY_TXN_STATE_ASSIGNED,
> > + DHCP_PROXY_TXN_STATE_INVALID,
> > + DHCP_PROXY_TXN_STATE_MAX=DHCP_PROXY_TXN_STATE_INVALID
> > +};
> > +
> > +struct dhcp_proxy_node
> > +{
> > + struct hmap_node hmap_node;
> > + struct eth_addr client_mac;
> > + uint64_t epoch_ms;
> > + uint64_t lease_time_ms;
> > + enum dhcp_proxy_txn_state txn_state;
> > + ovs_be32 txn_id;
> > + ovs_be32 client_ip;
> > + ovs_be32 proxy_ip;
> > + ovs_be32 server_ip;
> > +};
> > +
> > +#define DHCP_MSG_TYPE_MAX (OVN_DHCP_MSG_RELEASE+1)
> > +struct dhcp_proxy_info
> > +{
> > + struct hmap cache_hmap;
> > + uint32_t secret;
> > + enum dhcp_proxy_txn_state
> > sm[DHCP_PROXY_TXN_STATE_MAX][DHCP_MSG_TYPE_MAX];
> > +};
> > +
> > +static struct dhcp_proxy_info g_dhcp_proxy;
> > +
> > +static struct dhcp_proxy_info*
> > +dhcp_proxy_get(void)
> > +{
> > + return &g_dhcp_proxy;
> > +}
> > +
> > +static void
> > +dhcp_proxy_init(struct dhcp_proxy_info *dhcp_proxy)
> > +{
> > + hmap_init(&dhcp_proxy->cache_hmap);
> > + dhcp_proxy->secret = random_uint32();
> > +
> > + for (int i=DHCP_PROXY_TXN_STATE_INIT; i<DHCP_PROXY_TXN_STATE_MAX; i++)
> > + {
> > + for (int j=0; j<DHCP_MSG_TYPE_MAX; j++)
> > + {
> > + dhcp_proxy->sm[i][j] = DHCP_PROXY_TXN_STATE_INVALID;
> > + }
> > + }
> > +
> > + //TODO: Validate state machine for all possible cases
> > + enum dhcp_proxy_txn_state *dhcp_sm_state =
> > +
> > dhcp_proxy->sm[DHCP_PROXY_TXN_STATE_INIT];
> > + dhcp_sm_state[DHCP_MSG_DISCOVER] = DHCP_PROXY_TXN_STATE_DISCOVER;
> > + dhcp_sm_state[DHCP_MSG_OFFER] = DHCP_PROXY_TXN_STATE_INIT;
> > + dhcp_sm_state[DHCP_MSG_REQUEST] = DHCP_PROXY_TXN_STATE_REQUEST;
> > + dhcp_sm_state[OVN_DHCP_MSG_DECLINE] = DHCP_PROXY_TXN_STATE_INIT;
> > + dhcp_sm_state[DHCP_MSG_ACK] = DHCP_PROXY_TXN_STATE_INIT;
> > + dhcp_sm_state[DHCP_MSG_NAK] = DHCP_PROXY_TXN_STATE_INIT;
> > + dhcp_sm_state[OVN_DHCP_MSG_RELEASE] = DHCP_PROXY_TXN_STATE_INIT;
> > +
> > + dhcp_sm_state = dhcp_proxy->sm[DHCP_PROXY_TXN_STATE_DISCOVER];
> > + dhcp_sm_state[DHCP_MSG_DISCOVER] = DHCP_PROXY_TXN_STATE_DISCOVER;
> > + dhcp_sm_state[DHCP_MSG_OFFER] = DHCP_PROXY_TXN_STATE_OFFER;
> > +
> > + dhcp_sm_state = dhcp_proxy->sm[DHCP_PROXY_TXN_STATE_OFFER];
> > + dhcp_sm_state[DHCP_MSG_DISCOVER] = DHCP_PROXY_TXN_STATE_DISCOVER;
> > + dhcp_sm_state[DHCP_MSG_REQUEST] = DHCP_PROXY_TXN_STATE_REQUEST;
> > + dhcp_sm_state[OVN_DHCP_MSG_DECLINE] = DHCP_PROXY_TXN_STATE_INIT;
> > + dhcp_sm_state[DHCP_MSG_OFFER] = DHCP_PROXY_TXN_STATE_OFFER;
> > +
> > + dhcp_sm_state = dhcp_proxy->sm[DHCP_PROXY_TXN_STATE_REQUEST];
> > + dhcp_sm_state[DHCP_MSG_DISCOVER] = DHCP_PROXY_TXN_STATE_DISCOVER;
> > + dhcp_sm_state[DHCP_MSG_REQUEST] = DHCP_PROXY_TXN_STATE_REQUEST;
> > + dhcp_sm_state[DHCP_MSG_OFFER] = DHCP_PROXY_TXN_STATE_OFFER;
> > + dhcp_sm_state[DHCP_MSG_ACK] = DHCP_PROXY_TXN_STATE_ASSIGNED;
> > + dhcp_sm_state[DHCP_MSG_NAK] = DHCP_PROXY_TXN_STATE_INIT;
> > +
> > + dhcp_sm_state = dhcp_proxy->sm[DHCP_PROXY_TXN_STATE_ASSIGNED];
> > + dhcp_sm_state[DHCP_MSG_DISCOVER] = DHCP_PROXY_TXN_STATE_DISCOVER;
> > + dhcp_sm_state[DHCP_MSG_REQUEST] = DHCP_PROXY_TXN_STATE_REQUEST;
> > + dhcp_sm_state[OVN_DHCP_MSG_RELEASE] = DHCP_PROXY_TXN_STATE_INIT;
> > +}
> > +
> > +static bool
> > +dhcp_proxy_is_msg_type_supported(uint8_t msg_type)
> > +{
> > + return (msg_type >= DHCP_MSG_DISCOVER && msg_type <=
> > OVN_DHCP_MSG_RELEASE);
> > +}
> > +
> > +static bool
> > +dhcp_proxy_is_txn_state_valid(enum dhcp_proxy_txn_state state)
> > +{
> > + return (state >= DHCP_PROXY_TXN_STATE_INIT
> > + && state < DHCP_PROXY_TXN_STATE_INVALID);
> > +}
> > +
> > +static enum dhcp_proxy_txn_state
> > +dhcp_proxy_next_txn_state(enum dhcp_proxy_txn_state
> > sm[][DHCP_MSG_TYPE_MAX],
> > + enum dhcp_proxy_txn_state curr_state, uint8_t msg_type)
> > +{
> > + if (!(dhcp_proxy_is_msg_type_supported(msg_type)
> > + && dhcp_proxy_is_txn_state_valid(curr_state)))
> > + {
> > + return DHCP_PROXY_TXN_STATE_INVALID;
> > + }
> > + return sm[curr_state][msg_type];
> > +}
> > +
> > +static uint64_t
> > +dhcp_proxy_hash(struct eth_addr ea, uint32_t basis)
> > +{
> > + return hash_uint64_basis(eth_addr_to_uint64(ea), basis);
> > +}
> > +
> > +static void
> > +dhcp_proxy_insert(struct dhcp_proxy_info *dhcp_proxy,
> > + struct dhcp_proxy_node *node, uint64_t hash)
> > +{
> > + hmap_insert(&dhcp_proxy->cache_hmap, &node->hmap_node, hash);
> > +}
> > +
> > +static struct dhcp_proxy_node*
> > +dhcp_proxy_lookup(struct dhcp_proxy_info *dhcp_proxy, struct eth_addr ea)
> > +{
> > + struct dhcp_proxy_node *node;
> > + HMAP_FOR_EACH_WITH_HASH(node, hmap_node,
> > + dhcp_proxy_hash(ea, dhcp_proxy->secret),
> > &dhcp_proxy->cache_hmap)
> > + {
> > + if (eth_addr_equals(node->client_mac, ea))
> > + {
> > + return node;
> > + }
> > + }
> > + return NULL;
> > +}
> > +
> > +static struct dhcp_proxy_node*
> > +dhcp_proxy_lookup_match_xid(struct dhcp_proxy_info *dhcp_proxy, struct
> > eth_addr ea,
> > + uint32_t dhcp_txn_id)
> > +{
> > + struct dhcp_proxy_node *node = dhcp_proxy_lookup(dhcp_proxy, ea);
> > + if (node && (node->txn_id == dhcp_txn_id))
> > + {
> > + return node;
> > + }
> > + return NULL;
> > +}
> > +
> > +//TODO: yet to implement when and how nodes are deleted
> > +static void
> > +dhcp_proxy_delete_node(struct dhcp_proxy_info *dhcp_proxy OVS_UNUSED,
> > + struct eth_addr ea OVS_UNUSED)
> > +{
> > +}
> > +
> > +//time_wall_msec();
> > +
> > +/* Called with in the pinctrl_handler thread context. */
> > +static void
> > +pinctrl_handle_dhcp_proxy_req(
> > + 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;
> > +
> > + /* Parse relay IP and server IP. */
> > + ovs_be32 *proxy_ip = ofpbuf_try_pull(userdata, sizeof *proxy_ip);
> > + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof *server_ip);
> > + if (!proxy_ip || !server_ip) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: proxy ip or server ip not
> > present in the userdata");
> > + return;
> > + }
> > +
> > + /* Validate the DHCP request packet.
> > + * Format of the DHCP packet is
> > + *
> > ------------------------------------------------------------------------
> > + *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var
> > len)|
> > + *
> > ------------------------------------------------------------------------
> > + */
> > +
> > + 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 = 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, "DHCP_PROXY_REQ: invalid or incomplete DHCP
> > packet received");
> > + return;
> > + }
> > +
> > + 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, "DHCP_PROXY_REQ: invalid or incomplete DHCP
> > packet received, "
> > + "bad data length");
> > + return;
> > + }
> > + 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_PROXY_REQ: invalid opcode in the DHCP
> > packet: %d",
> > + in_dhcp_data->op);
> > + return;
> > + }
> > +
> > + /* 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_PROXY_REQ: magic cookie not present in the
> > packet");
> > + return;
> > + }
> > +
> > + if (in_dhcp_data->giaddr) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: giaddr is already set");
> > + return;
> > + }
> > +
> > + if (in_dhcp_data->htype != 0x1) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: packet is recieved with
> > unsupported hardware type");
> > + return;
> > + }
> > +
> > + ovs_be32 *server_id_ptr = NULL;
> > + const uint8_t *in_dhcp_msg_type = NULL;
> > +
> > + in_dhcp_ptr += sizeof magic_cookie;
> > + 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 OVN_DHCP_OPT_CODE_SERVER_ID: //Server Identifier
> > + if (in_dhcp_opt->len == 4) {
> > + server_id_ptr = DHCP_OPT_PAYLOAD(in_dhcp_opt);
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > + }
> > +
> > + /* 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_PROXY_REQ: missing message type");
> > + return;
> > + }
> > +
> > + struct eth_addr ea;
> > + struct dhcp_proxy_node *node;
> > + struct dhcp_proxy_info *dhcp_proxy = dhcp_proxy_get();
> > + memcpy(ea.ea, in_dhcp_data->chaddr, sizeof(ea));
> > +
> > + if((node = dhcp_proxy_lookup_match_xid(dhcp_proxy, ea,
> > in_dhcp_data->xid))
> > + != NULL) {
> > + enum dhcp_proxy_txn_state nxt_state = dhcp_proxy_next_txn_state(
> > + dhcp_proxy->sm,
> > + node->txn_state,
> > + *in_dhcp_msg_type);
> > + if (nxt_state < DHCP_PROXY_TXN_STATE_INVALID) {
> > + if (server_id_ptr && *server_id_ptr != *proxy_ip) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1,
> > 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: server identifier
> > mismatch");
> > + return;
> > + }
> > + node->txn_state = nxt_state;
> > + node->epoch_ms = time_wall_msec();
> > + }
> > + else {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: invalid state transistion"
> > + " for existing session");
> > + return;
> > + }
> > + }
> > + else {
> > + enum dhcp_proxy_txn_state nxt_state = dhcp_proxy_next_txn_state(
> > + dhcp_proxy->sm,
> > + DHCP_PROXY_TXN_STATE_INIT,
> > + *in_dhcp_msg_type);
> > + if (nxt_state < DHCP_PROXY_TXN_STATE_INVALID) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: New session");
> > + node = xmalloc(sizeof(*node));
> > + node->client_mac = ea;
> > + node->client_ip = in_dhcp_data->ciaddr;
> > + node->txn_id = in_dhcp_data->xid;
> > + node->txn_state = nxt_state;
> > + node->epoch_ms = time_wall_msec();
> > + node->server_ip = *server_ip;
> > + node->proxy_ip = *proxy_ip;
> > + dhcp_proxy_insert(dhcp_proxy, node,
> > + dhcp_proxy_hash(ea, dhcp_proxy->secret));
> > + }
> > + else {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_REQ: invalid state transistion"
> > + " for new session");
> > + return;
> > + }
> > + }
> > +
> > + /* Proxy the DHCP request packet */
> > +
> > + /* Replace Server Identifier Option */
> > + if (server_id_ptr) {
> > + put_unaligned_be32(server_id_ptr, *server_ip);
> > + }
> > +
> > + 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 ip_header *out_ip = dp_packet_l3(&pkt_out);
> > +
> > + 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);
> > + dhcp_data->giaddr = *proxy_ip;
> > + //TODO: incremental checkcum
> > + if (udp->udp_csum) {
> > + udp->udp_csum = 0;
> > + uint32_t p_csum = packet_csum_pseudoheader(out_ip);
> > + udp->udp_csum = csum_finish(csum_continue(p_csum, udp,
> > new_l4_size));
> > + }
> > + pin->packet = dp_packet_data(&pkt_out);
> > + pin->packet_len = dp_packet_size(&pkt_out);
> > +
> > + 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_proxy_resp(
> > + 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;
> > +
> > + /* Parse relay IP and server IP. */
> > + ovs_be32 *proxy_ip = ofpbuf_try_pull(userdata, sizeof *proxy_ip);
> > + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof *server_ip);
> > + if (!proxy_ip || !server_ip) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: proxy ip or server ip not
> > present in the userdata");
> > + return;
> > + }
> > +
> > + /* Validate the DHCP request packet.
> > + * Format of the DHCP packet is
> > + *
> > ------------------------------------------------------------------------
> > + *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var
> > len)|
> > + *
> > ------------------------------------------------------------------------
> > + */
> > +
> > + 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 = 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, "DHCP_PROXY_RESP: invalid or incomplete packet
> > received");
> > + return;
> > + }
> > +
> > + 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, "DHCP_PROXY_RESP: invalid or incomplete packet
> > received, "
> > + "bad data length");
> > + return;
> > + }
> > + 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_PROXY_RESP:: invalid opcode in the packet:
> > %d",
> > + in_dhcp_data->op);
> > + return;
> > + }
> > +
> > + /* 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_PROXY_RESP:: magic cookie not present in
> > the packet");
> > + return;
> > + }
> > +
> > + if (!in_dhcp_data->giaddr) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: giaddr is not set in
> > response");
> > + return;
> > + }
> > +
> > + ovs_be32 *server_id_ptr = NULL;
> > + ovs_be32 lease_time = 0;
> > + ovs_be32 giaddr = in_dhcp_data->giaddr;
> > + const uint8_t *in_dhcp_msg_type = NULL;
> > +
> > + in_dhcp_ptr += sizeof magic_cookie;
> > + 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 OVN_DHCP_OPT_CODE_SERVER_ID: //Server Identifier
> > + if (in_dhcp_opt->len == 4) {
> > + server_id_ptr = DHCP_OPT_PAYLOAD(in_dhcp_opt);
> > + }
> > + break;
> > + case OVN_DHCP_OPT_CODE_LEASE_TIME:
> > + if (in_dhcp_opt->len == 4) {
> > + lease_time =
> > get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > + }
> > +
> > + /* 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_PROXY_RESP: missing message type");
> > + return;
> > + }
> > +
> > + if (!server_id_ptr) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: missing server identifier");
> > + return;
> > + }
> > +
> > + if (*server_id_ptr != *server_ip) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: server identifier mismatch");
> > + return;
> > + }
> > +
> > + if (giaddr != *proxy_ip) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: giaddr mismatch");
> > + return;
> > + }
> > +
> > + struct eth_addr ea;
> > + struct dhcp_proxy_node *node;
> > + struct dhcp_proxy_info *dhcp_proxy = dhcp_proxy_get();
> > + memcpy(ea.ea, in_dhcp_data->chaddr, sizeof(ea));
> > +
> > + if((node = dhcp_proxy_lookup_match_xid(dhcp_proxy, ea,
> > in_dhcp_data->xid))
> > + != NULL) {
> > + enum dhcp_proxy_txn_state nxt_state = dhcp_proxy_next_txn_state(
> > + dhcp_proxy->sm,
> > + node->txn_state,
> > + *in_dhcp_msg_type);
> > + if (nxt_state < DHCP_PROXY_TXN_STATE_INVALID) {
> > + //TODO: Check IP assigned by server is falling in giaddr subnet
> > + if (nxt_state == DHCP_PROXY_TXN_STATE_ASSIGNED) {
> > + node->client_ip = in_dhcp_data->yiaddr;
> > + }
> > + node->txn_state = nxt_state;
> > + node->epoch_ms = time_wall_msec();
> > + node->lease_time_ms = node->epoch_ms +
> > ntohl(lease_time)*60*1000;
> > + }
> > + else {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: invalid state transistion"
> > + " for existing session");
> > + return;
> > + }
> > + }
> > + else {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP: transaction not found");
> > + return;
> > + }
> > +
> > + /* Proxy the DHCP response packet */
> > +
> > + /* Replace Server Identifier Option */
> > + if (server_id_ptr) {
> > + put_unaligned_be32(server_id_ptr, *proxy_ip);
> > + }
> > +
> > + 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);
> > + dhcp_data->giaddr = htonl(0x0);
> > + 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_orig, ip_dst;
> > + if (!is_dhcp_flags_broadcast(dhcp_data->flags)) {
> > + ip_dst = dhcp_data->yiaddr;
> > + } else {
> > + ip_dst = htonl(0xffffffff);
> > + }
> > + ip_dst_orig = get_16aligned_be32(&out_ip->ip_dst);
> > + if (ip_dst_orig != ip_dst)
> > + {
> > + 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);
> > + }
> > + }
> > + //TODO: incremental checkcum
> > + if (udp->udp_csum) {
> > + udp->udp_csum = 0;
> > + uint32_t p_csum = packet_csum_pseudoheader(out_ip);
> > + udp->udp_csum = csum_finish(csum_continue(p_csum, udp,
> > new_l4_size));
> > + }
> > +
> > + pin->packet = dp_packet_data(&pkt_out);
> > + pin->packet_len = dp_packet_size(&pkt_out);
> > +
> > + 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_proxy_resp_fwd(
> > + struct rconn *swconn,
> > + struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
> > + 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;
> > +
> > + /* Validate the DHCP request packet.
> > + * Format of the DHCP packet is
> > + *
> > ------------------------------------------------------------------------
> > + *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var
> > len)|
> > + *
> > ------------------------------------------------------------------------
> > + */
> > +
> > + 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 = 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, "DHCP_PROXY_RESP_FWD: invalid or incomplete
> > packet received");
> > + return;
> > + }
> > +
> > + 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, "DHCP_PROXY_RESP_FWD: invalid or incomplete
> > packet received, "
> > + "bad data length");
> > + return;
> > + }
> > + 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_PROXY_RESP_FWD: invalid opcode in the
> > packet: %d",
> > + in_dhcp_data->op);
> > + return;
> > + }
> > +
> > + /* 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_PROXY_RESP_FWD: magic cookie not present
> > in the packet");
> > + return;
> > + }
> > +
> > + if (!in_dhcp_data->giaddr) {
> > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> > + VLOG_WARN_RL(&rl, "DHCP_PROXY_RESP_FWD: giaddr is not set in
> > request");
> > + return;
> > + }
> > +
> > + /* 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);
> > +
> > + 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(
> > @@ -3105,6 +3856,21 @@ process_packet_in(struct rconn *swconn, const struct
> > ofp_header *msg)
> > ovs_mutex_unlock(&pinctrl_mutex);
> > break;
> >
> > + case ACTION_OPCODE_DHCP_PROXY_REQ:
> > + pinctrl_handle_dhcp_proxy_req(swconn, &packet, &pin,
> > + &userdata, &continuation);
> > + break;
> > +
> > + case ACTION_OPCODE_DHCP_PROXY_RESP:
> > + pinctrl_handle_dhcp_proxy_resp(swconn, &packet, &pin,
> > + &userdata, &continuation);
> > + break;
> > +
> > + case ACTION_OPCODE_DHCP_PROXY_RESP_FWD:
> > + pinctrl_handle_dhcp_proxy_resp_fwd(swconn, &packet, &pin,
> > + &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 23a919049..97b074b3e 100644
> > --- a/include/ovn/actions.h
> > +++ b/include/ovn/actions.h
> > @@ -95,6 +95,9 @@ 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_PROXY_REQ, ovnact_dhcp_proxy) \
> > + OVNACT(DHCPV4_PROXY_RESP, ovnact_dhcp_proxy) \
> > + OVNACT(DHCPV4_PROXY_RESP_FWD, ovnact_dhcp_proxy) \
> > OVNACT(SET_QUEUE, ovnact_set_queue) \
> > OVNACT(DNS_LOOKUP, ovnact_result) \
> > OVNACT(LOG, ovnact_log) \
> > @@ -386,6 +389,14 @@ struct ovnact_put_opts {
> > size_t n_options;
> > };
> >
> > +/* OVNACT_DHCP_PROXY. */
> > +struct ovnact_dhcp_proxy {
> > + struct ovnact ovnact;
> > + int family;
> > + ovs_be32 proxy_ipv4;
> > + ovs_be32 server_ipv4;
> > +};
> > +
> > /* Valid arguments to SET_QUEUE action.
> > *
> > * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues should
> > @@ -746,6 +757,28 @@ enum action_opcode {
> >
> > /* activation_strategy_rarp() */
> > ACTION_OPCODE_ACTIVATION_STRATEGY_RARP,
> > +
> > + /* "result = dhcp_proxy_req(proxy_ip, server_ip)".
> > + *
> > + * Arguments follow the action_header, in this format:
> > + * - The 32-bit DHCP proxy IP.
> > + * - The 32-bit DHCP server IP.
> > + */
> > + ACTION_OPCODE_DHCP_PROXY_REQ,
> > +
> > + /* "result = dhcp_proxy_resp(proxy_ip, server_ip)".
> > + *
> > + * Arguments follow the action_header, in this format:
> > + * - The 32-bit DHCP proxy IP.
> > + */
> > + ACTION_OPCODE_DHCP_PROXY_RESP,
> > +
> > + /* "result = dhcp_proxy_resp_fwd()".
> > + *
> > + * Arguments follow the action_header, in this format:
> > + * - The 32-bit DHCP proxy IP.
> > + */
> > + ACTION_OPCODE_DHCP_PROXY_RESP_FWD,
> > };
> >
> > /* Header. */
> > diff --git a/lib/actions.c b/lib/actions.c
> > index f89ccc6bf..402d6cbde 100644
> > --- a/lib/actions.c
> > +++ b/lib/actions.c
> > @@ -2629,6 +2629,143 @@ ovnact_controller_event_free(struct
> > ovnact_controller_event *event)
> > free_gen_options(event->options, event->n_options);
> > }
> >
> > +static void
> > +format_DHCPV4_PROXY_REQ(const struct ovnact_dhcp_proxy *dhcp_proxy, struct
> > ds *s)
> > +{
> > + ds_put_format(s, "dhcp_proxy_req("IP_FMT","IP_FMT");",
> > + IP_ARGS(dhcp_proxy->proxy_ipv4),
> > + IP_ARGS(dhcp_proxy->server_ipv4));
> > +}
> > +
> > +static void
> > +parse_dhcp_proxy_req(struct action_context *ctx,
> > + struct ovnact_dhcp_proxy *dhcp_proxy)
> > +{
> > + //lexer_get(ctx->lexer); /* Skip dhcp_proxy_req. */
> > + lexer_force_match(ctx->lexer, LEX_T_LPAREN);
> > +
> > + /* Parse proxy ip and server ip. */
> > + if (ctx->lexer->token.format == LEX_F_IPV4) {
> > + dhcp_proxy->family = AF_INET;
> > + dhcp_proxy->proxy_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_proxy->family = AF_INET;
> > + dhcp_proxy->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 proxy and
> > server ips");
> > + return;
> > + }
> > + lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> > +}
> > +
> > +static void
> > +encode_DHCPV4_PROXY_REQ(const struct ovnact_dhcp_proxy *dhcp_proxy,
> > + const struct ovnact_encode_params *ep,
> > + struct ofpbuf *ofpacts)
> > +{
> > + size_t oc_offset =
> > encode_start_controller_op(ACTION_OPCODE_DHCP_PROXY_REQ,
> > + true, ep->ctrl_meter_id,
> > + ofpacts);
> > + ofpbuf_put(ofpacts, &dhcp_proxy->proxy_ipv4,
> > sizeof(dhcp_proxy->proxy_ipv4));
> > + ofpbuf_put(ofpacts, &dhcp_proxy->server_ipv4,
> > sizeof(dhcp_proxy->server_ipv4));
> > + encode_finish_controller_op(oc_offset, ofpacts);
> > +}
> > +
> > +static void
> > +format_DHCPV4_PROXY_RESP(const struct ovnact_dhcp_proxy *dhcp_proxy,
> > struct ds *s)
> > +{
> > + ds_put_format(s, "dhcp_proxy_resp("IP_FMT");",
> > + IP_ARGS(dhcp_proxy->proxy_ipv4));
> > +}
> > +
> > +static void
> > +parse_dhcp_proxy_resp(struct action_context *ctx,
> > + struct ovnact_dhcp_proxy *dhcp_proxy)
> > +{
> > + //lexer_get(ctx->lexer); /* Skip dhcp_proxy_resp. */
> > + lexer_force_match(ctx->lexer, LEX_T_LPAREN);
> > +
> > + /* Parse proxy ip and server ip. */
> > + if (ctx->lexer->token.format == LEX_F_IPV4) {
> > + dhcp_proxy->family = AF_INET;
> > + dhcp_proxy->proxy_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_proxy->family = AF_INET;
> > + dhcp_proxy->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 proxy and
> > server ips");
> > + return;
> > + }
> > + lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> > +}
> > +
> > +static void
> > +encode_DHCPV4_PROXY_RESP(const struct ovnact_dhcp_proxy *dhcp_proxy,
> > + const struct ovnact_encode_params *ep,
> > + struct ofpbuf *ofpacts)
> > +{
> > + size_t oc_offset =
> > encode_start_controller_op(ACTION_OPCODE_DHCP_PROXY_RESP,
> > + true, ep->ctrl_meter_id,
> > + ofpacts);
> > + ofpbuf_put(ofpacts, &dhcp_proxy->proxy_ipv4,
> > sizeof(dhcp_proxy->proxy_ipv4));
> > + ofpbuf_put(ofpacts, &dhcp_proxy->server_ipv4,
> > sizeof(dhcp_proxy->server_ipv4));
> > + encode_finish_controller_op(oc_offset, ofpacts);
> > +}
> > +
> > +static void
> > +format_DHCPV4_PROXY_RESP_FWD(const struct ovnact_dhcp_proxy *dhcp_proxy,
> > struct ds *s)
> > +{
> > + ds_put_format(s, "dhcp_proxy_resp_fwd("IP_FMT");",
> > + IP_ARGS(dhcp_proxy->proxy_ipv4));
> > +}
> > +
> > +static void
> > +parse_dhcp_proxy_resp_fwd(struct action_context *ctx,
> > + struct ovnact_dhcp_proxy *dhcp_proxy OVS_UNUSED)
> > +{
> > + //lexer_get(ctx->lexer); /* Skip dhcp_proxy_resp_fwd. */
> > + lexer_force_match(ctx->lexer, LEX_T_LPAREN);
> > + lexer_force_match(ctx->lexer, LEX_T_RPAREN);
> > +}
> > +
> > +static void
> > +encode_DHCPV4_PROXY_RESP_FWD(
> > + const struct ovnact_dhcp_proxy *dhcp_proxy OVS_UNUSED,
> > + const struct ovnact_encode_params *ep,
> > + struct ofpbuf *ofpacts)
> > +{
> > + size_t oc_offset =
> > encode_start_controller_op(ACTION_OPCODE_DHCP_PROXY_RESP_FWD,
> > + true, ep->ctrl_meter_id,
> > + ofpacts);
> > + encode_finish_controller_op(oc_offset, ofpacts);
> > +}
> > +
> > +static void ovnact_dhcp_proxy_free(struct ovnact_dhcp_proxy *dhcp_proxy
> > 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,
> > @@ -5435,6 +5572,12 @@ parse_action(struct action_context *ctx)
> > parse_commit_lb_aff(ctx, ovnact_put_COMMIT_LB_AFF(ctx->ovnacts));
> > } else if (lexer_match_id(ctx->lexer, "sample")) {
> > parse_sample(ctx);
> > + } else if (lexer_match_id(ctx->lexer, "dhcp_proxy_req")) {
> > + parse_dhcp_proxy_req(ctx,
> > ovnact_put_DHCPV4_PROXY_REQ(ctx->ovnacts));
> > + } else if (lexer_match_id(ctx->lexer, "dhcp_proxy_resp")) {
> > + parse_dhcp_proxy_resp(ctx,
> > ovnact_put_DHCPV4_PROXY_RESP(ctx->ovnacts));
> > + } else if (lexer_match_id(ctx->lexer, "dhcp_proxy_resp_fwd")) {
> > + parse_dhcp_proxy_resp_fwd(ctx,
> > ovnact_put_DHCPV4_PROXY_RESP_FWD(ctx->ovnacts));
> > } else {
> > lexer_syntax_error(ctx->lexer, "expecting action");
> > }
> > diff --git a/lib/ovn-l7.h b/lib/ovn-l7.h
> > index 9dc331421..94c4955ff 100644
> > --- a/lib/ovn-l7.h
> > +++ b/lib/ovn-l7.h
> > @@ -69,6 +69,7 @@ struct gen_opts_map {
> > */
> > #define OVN_DHCP_OPT_CODE_NETMASK 1
> > #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 9a12a94ae..7e2fa1d22 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -157,8 +157,9 @@ enum ovn_stage {
> > PIPELINE_STAGE(SWITCH, OUT, QOS_MARK, 6, "ls_out_qos_mark") \
> > PIPELINE_STAGE(SWITCH, OUT, QOS_METER, 7, "ls_out_qos_meter") \
> > PIPELINE_STAGE(SWITCH, OUT, STATEFUL, 8, "ls_out_stateful") \
> > - PIPELINE_STAGE(SWITCH, OUT, CHECK_PORT_SEC, 9,
> > "ls_out_check_port_sec") \
> > - PIPELINE_STAGE(SWITCH, OUT, APPLY_PORT_SEC, 10,
> > "ls_out_apply_port_sec") \
> > + PIPELINE_STAGE(SWITCH, OUT, DHCP_PROXY_RESP, 9,
> > "ls_out_dhcp_proxy_resp") \
> > + PIPELINE_STAGE(SWITCH, OUT, CHECK_PORT_SEC, 10,
> > "ls_out_check_port_sec") \
> > + PIPELINE_STAGE(SWITCH, OUT, APPLY_PORT_SEC, 11,
> > "ls_out_apply_port_sec") \
> > \
> > /* Logical router ingress stages. */ \
> > PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
> > @@ -178,11 +179,12 @@ enum ovn_stage {
> > 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_PROXY_RESP_FWD, 17,
> > "lr_in_dhcp_proxy_resp_fwd") \
> > + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 18, "lr_in_arp_resolve")
> > \
> > + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 19, "lr_in_chk_pkt_len")
> > \
> > + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 20, "lr_in_larger_pkts")
> > \
> > + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 21, "lr_in_gw_redirect")
> > \
> > + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 22, "lr_in_arp_request")
> > \
> > \
> > /* Logical router egress stages. */ \
> > PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0,
> > \
> > @@ -9110,6 +9112,103 @@ build_dhcpv6_options_flows(struct ovn_port *op,
> > ds_destroy(&match);
> > }
> >
> > +static void
> > +build_lswitch_dhcp_proxy_flows(struct ovn_port *op,
> > + const struct hmap *lr_ports,
> > + const struct hmap *lflows,
> > + const struct shash *meter_groups OVS_UNUSED)
> > +{
> > + 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 || !op->od->nbs->dhcp_proxy_port) {
> > + return;
> > + }
> > +
> > + struct ds match = DS_EMPTY_INITIALIZER;
> > + struct ds action = DS_EMPTY_INITIALIZER;
> > + struct nbrec_logical_router_port *lrp = op->od->nbs->dhcp_proxy_port;
> > + struct ovn_port *rp = ovn_port_find(lr_ports, lrp->name);
> > +
> > + if (!rp || !rp->nbrp || !rp->nbrp->dhcp_proxy) {
> > + return;
> > + }
> > +
> > + struct ovn_port *sp = NULL;
> > + struct nbrec_dhcp_proxy *dhcp_proxy = rp->nbrp->dhcp_proxy;
> > +
> > + for (int i=0; i<op->od->n_router_ports; i++) {
> > + struct ovn_port *sp_tmp = op->od->router_ports[i];
> > + if (sp_tmp->peer == rp) {
> > + sp = sp_tmp;
> > + break;
> > + }
> > + }
> > + if (!sp) {
> > + return;
> > + }
> > +
> > + char *server_ip_str = NULL;
> > + uint16_t port;
> > + int addr_family;
> > + struct in6_addr server_ip;
> > +
> > + if (!ip_address_and_port_from_lb_key(dhcp_proxy->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_PROXY_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,
> > + &lrp->header_);
> > + ds_clear(&match);
> > + ds_clear(&action);
> > +
> > + ds_put_format(
> > + &match, "inport == %s && eth.src == %s && outport == %s &&"
> > + "ip4.src == %s && "
> > + "udp.src == 67 && udp.dst == 67",
> > + sp->json_key, rp->lrp_networks.ea_s, op->json_key, server_ip_str);
> > + ds_put_format(&action,
> > + "dhcp_proxy_resp(%s,%s);ip4.src=%s;udp.dst=68;next;"
> > + "/* DHCP_PROXY_RESP */",
> > + rp->lrp_networks.ipv4_addrs[0].addr_s,
> > + server_ip_str,
> > + rp->lrp_networks.ipv4_addrs[0].addr_s);
> > + ovn_lflow_add_with_hint__(lflows, op->od,
> > + S_SWITCH_OUT_DHCP_PROXY_RESP, 100,
> > + ds_cstr(&match),
> > + ds_cstr(&action),
> > + op->key,
> > + NULL,
> > + &lrp->header_);
> > + ds_clear(&match);
> > + ds_clear(&action);
> > + free(server_ip_str);
> > +}
> > +
> > static void
> > build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
> > const struct ovn_port
> > *port,
> > @@ -9672,6 +9771,13 @@ build_lswitch_dhcp_options_and_response(struct
> > ovn_port *op,
> > return;
> > }
> >
> > + if (op->od && op->od->nbs
> > + && op->od->nbs->dhcp_proxy_port) {
> > + /* Don't add the DHCP server flows if DHCP Proxy 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)) {
> > @@ -9719,6 +9825,7 @@ build_lswitch_dhcp_and_dns_defaults(struct
> > ovn_datapath *od,
> > ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
> > ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1", "next;");
> > ovn_lflow_add(lflows, od, S_SWITCH_IN_EXTERNAL_PORT, 0, "1", "next;");
> > + ovn_lflow_add(lflows, od, S_SWITCH_OUT_DHCP_PROXY_RESP, 0, "1",
> > "next;");
> > }
> >
> > /* Logical switch ingress table 22 and 23: DNS lookup and response
> > @@ -13918,6 +14025,100 @@ build_dhcpv6_reply_flows_for_lrouter_port(
> > }
> > }
> >
> > +static void
> > +build_dhcp_proxy_flows_for_lrouter_port(
> > + struct ovn_port *op, struct hmap *lflows,
> > + struct ds *match)
> > +{
> > + if (!op->nbrp || !op->nbrp->dhcp_proxy) {
> > + return;
> > + }
> > + struct nbrec_dhcp_proxy *dhcp_proxy = op->nbrp->dhcp_proxy;
> > + if (!dhcp_proxy->servers) {
> > + return;
> > + }
> > +
> > + int addr_family;
> > + uint16_t port;
> > + char *server_ip_str = NULL;
> > + struct in6_addr server_ip;
> > +
> > + if (!ip_address_and_port_from_lb_key(dhcp_proxy->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,
> > + "dhcp_proxy_req(%s,%s);"
> > + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_PROXY_REQ
> > */",
> > + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str,
> > + 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_);
> > +
> > + ds_clear(match);
> > + ds_clear(&dhcp_action);
> > +
> > + ds_put_format(
> > + match, "inport == %s && "
> > + "ip4.dst == %s && "
> > + "udp.src == 68 && udp.dst == 67",
> > + op->json_key, op->lrp_networks.ipv4_addrs[0].addr_s);
> > +
> > + ds_put_format(&dhcp_action,
> > + "dhcp_proxy_req(%s,%s);"
> > + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_PROXY_REQ
> > */",
> > + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str,
> > + 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_);
> > + 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_PROXY_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_);
> > +
> > + 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,
> > + "dhcp_proxy_resp_fwd();outport=%s;output; /* DHCP_PROXY_RESP */",
> > + op->json_key);
> > + ovn_lflow_add_with_hint(lflows, op->od,
> > S_ROUTER_IN_DHCP_PROXY_RESP_FWD,
> > + 110,
> > + ds_cstr(match), ds_cstr(&dhcp_action),
> > + &op->nbrp->header_);
> > +
> > + 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 hmap *lflows,
> > @@ -15134,6 +15335,7 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath
> > *od, struct hmap *lflows,
> > ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, 0, "1", "next;");
> > ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
> > ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;");
> > + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_PROXY_RESP_FWD, 0, "1",
> > "next;");
> >
> > const char *ct_flag_reg = features->ct_no_masked_label
> > ? "ct_mark"
> > @@ -15614,6 +15816,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct
> > ovn_port *op,
> > build_lswitch_dhcp_options_and_response(op, lflows, meter_groups);
> > build_lswitch_external_port(op, lflows);
> > build_lswitch_ip_unicast_lookup(op, lflows, actions, match);
> > + build_lswitch_dhcp_proxy_flows(op, lr_ports, lflows, meter_groups);
> >
> > /* Build Logical Router Flows. */
> > build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows);
> > @@ -15643,6 +15846,7 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct
> > ovn_port *op,
> > build_egress_delivery_flows_for_lrouter_port(op, lsi->lflows,
> > &lsi->match,
> > &lsi->actions);
> > build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match);
> > + build_dhcp_proxy_flows_for_lrouter_port(op, lsi->lflows, &lsi->match);
> > build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows,
> > &lsi->match, &lsi->actions,
> > lsi->meter_groups);
> > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
> > index f8bac5302..d448e2bfe 100644
> > --- a/ovn-nb.ovsschema
> > +++ b/ovn-nb.ovsschema
> > @@ -1,7 +1,7 @@
> > {
> > "name": "OVN_Northbound",
> > "version": "7.0.4",
> > - "cksum": "1676649201 33795",
> > + "cksum": "2503700777 34818",
> > "tables": {
> > "NB_Global": {
> > "columns": {
> > @@ -89,7 +89,12 @@
> > "type": {"key": {"type": "uuid",
> > "refTable": "Forwarding_Group",
> > "refType": "strong"},
> > - "min": 0, "max": "unlimited"}}},
> > + "min": 0, "max": "unlimited"}},
> > + "dhcp_proxy_port": {"type": {"key": {"type": "uuid",
> > + "refTable":
> > "Logical_Router_Port",
> > + "refType": "weak"},
> > + "min": 0,
> > + "max": 1}}},
> > "isRoot": true},
> > "Logical_Switch_Port": {
> > "columns": {
> > @@ -436,6 +441,11 @@
> > "ipv6_prefix": {"type": {"key": "string",
> > "min": 0,
> > "max": "unlimited"}},
> > + "dhcp_proxy": {"type": {"key": {"type": "uuid",
> > + "refTable": "DHCP_Proxy",
> > + "refType": "weak"},
> > + "min": 0,
> > + "max": 1}},
> > "external_ids": {
> > "type": {"key": "string", "value": "string",
> > "min": 0, "max": "unlimited"}}},
> > @@ -526,6 +536,15 @@
> > "type": {"key": "string", "value": "string",
> > "min": 0, "max": "unlimited"}}},
> > "isRoot": true},
> > + "DHCP_Proxy": {
> > + "columns": {
> > + "servers": {"type": {"key": "string",
> > + "min": 0,
> > + "max": 1}},
> > + "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 4fbf4f7e5..00e4c41fc 100644
> > --- a/ovn-nb.xml
> > +++ b/ovn-nb.xml
> > @@ -572,6 +572,11 @@
> > Please see the <ref table="DNS"/> table.
> > </column>
> >
> > + <column name="dhcp_proxy_port">
> > + This column defines the <ref table="Logical_Router_Port"/> on which
> > + DHCP proxy is enabled.
> > + </column>
> > +
> > <column name="forwarding_groups">
> > Groups a set of logical port endpoints for traffic going out of the
> > logical switch.
> > @@ -2944,6 +2949,10 @@ or
> > port has all ingress and egress traffic dropped.
> > </column>
> >
> > + <column name="dhcp_proxy">
> > + This column is used to enabled DHCP Proxy. Please refer to <ref
> > table="DHCP_Proxy"/> table.
> > + </column>
> > +
> > <group title="Distributed Gateway Ports">
> > <p>
> > Gateways, as documented under <code>Gateways</code> in the OVN
> > @@ -4231,6 +4240,24 @@ or
> > </group>
> > </table>
> >
> > + <table name="DHCP_Proxy" title="DHCP Proxy">
> > + <p>
> > + OVN implements native DHCPv4 proxy support which caters to the common
> > + use case of proxying the DHCP requests to external DHCP server.
> > + </p>
> > +
> > + <column name="servers">
> > + <p>
> > + The DHCPv4 server IP address.
> > + </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/ovn-northd.at b/tests/ovn-northd.at
> > index d5be3be75..ace0c5c34 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -8206,6 +8206,8 @@ check ovn-nbctl --wait=sb ls-add sw0
> >
> > ovn-sbctl dump-flows sw0 > sw0flows
> > AT_CAPTURE_FILE([sw0flows])
> > +echo "FLOWS"
> > +cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e ls_in_l2_unknown |
> > sort | sed 's/table=../table=??/'
> >
> > AT_CHECK([cat sw0flows | grep -e port_sec -e ls_in_l2_lkup -e
> > ls_in_l2_unknown | \
> > sort | sed 's/table=../table=??/' ], [0], [dnl
> > @@ -8214,6 +8216,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=50 , match=(1),
> > action=(reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport
> > = get_fdb(eth.dst); next;)
> > @@ -8221,8 +8225,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_lkup ), priority=70 , match=(eth.mcast),
> > action=(outport = "_MC_flood"; output;)
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > check ovn-nbctl lsp-add sw0 sw0p1 -- lsp-set-addresses sw0p1
> > "00:00:00:00:00:01"
> > @@ -8239,6 +8241,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=50 , match=(1),
> > action=(reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport
> > = get_fdb(eth.dst); next;)
> > @@ -8248,8 +8252,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_lkup ), priority=70 , match=(eth.mcast),
> > action=(outport = "_MC_flood"; output;)
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > check ovn-nbctl lsp-set-port-security sw0p1 "00:00:00:00:00:01 10.0.0.3
> > 1000::3"
> > @@ -8265,6 +8267,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=50 , match=(1),
> > action=(reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport
> > = get_fdb(eth.dst); next;)
> > @@ -8274,8 +8278,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_lkup ), priority=70 , match=(eth.mcast),
> > action=(outport = "_MC_flood"; output;)
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > # Disable sw0p1
> > @@ -8292,6 +8294,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=50 , match=(1),
> > action=(reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > table=??(ls_in_l2_lkup ), priority=0 , match=(1), action=(outport
> > = get_fdb(eth.dst); next;)
> > @@ -8302,8 +8306,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport ==
> > "sw0p1"), action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > check ovn-nbctl --wait=sb lsp-set-options sw0p2 qdisc_queue_id=10
> > @@ -8319,6 +8321,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=70 , match=(inport ==
> > "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=110 , match=(outport ==
> > "localnetport" && inport == "sw0p2"), action=(set_queue(10); output;)
> > table=??(ls_out_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > @@ -8330,8 +8334,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport ==
> > "sw0p1"), action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > check ovn-nbctl set logical_switch_port sw0p1 enabled=true
> > @@ -8350,6 +8352,8 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_check_port_sec), priority=70 , match=(inport ==
> > "sw0p2"), action=(set_queue(10); reg0[[15]] = check_in_port_sec(); next;)
> > table=??(ls_in_apply_port_sec), priority=0 , match=(1), action=(next;)
> > table=??(ls_in_apply_port_sec), priority=50 , match=(reg0[[15]] == 1),
> > action=(drop;)
> > + table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > + table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > table=??(ls_out_apply_port_sec), priority=0 , match=(1),
> > action=(output;)
> > table=??(ls_out_apply_port_sec), priority=100 , match=(outport ==
> > "localnetport"), action=(set_queue(10); output;)
> > table=??(ls_out_apply_port_sec), priority=110 , match=(outport ==
> > "localnetport" && inport == "sw0p2"), action=(set_queue(10); output;)
> > @@ -8361,8 +8365,6 @@ sort | sed 's/table=../table=??/' ], [0], [dnl
> > table=??(ls_in_l2_lkup ), priority=70 , match=(eth.mcast),
> > action=(outport = "_MC_flood"; output;)
> > table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
> > table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"),
> > action=(drop;)
> > - table=??(ls_out_check_port_sec), priority=0 , match=(1),
> > action=(reg0[[15]] = check_out_port_sec(); next;)
> > - table=??(ls_out_check_port_sec), priority=100 , match=(eth.mcast),
> > action=(reg0[[15]] = 0; next;)
> > ])
> >
> > AT_CLEANUP
> > @@ -8684,9 +8686,9 @@ ovn-nbctl --wait=sb set logical_router_port R1-PUB
> > options:redirect-type=bridged
> > ovn-sbctl dump-flows R1 > R1flows
> > AT_CAPTURE_FILE([R1flows])
> >
> > -AT_CHECK([grep "lr_in_arp_resolve" R1flows | grep priority=90 | sort],
> > [0], [dnl
> > - table=17(lr_in_arp_resolve ), priority=90 , match=(outport ==
> > "R1-PUB" && ip4.src == 10.0.0.3 && is_chassis_resident("S0-P0")),
> > action=(get_arp(outport, reg0); next;)
> > - table=17(lr_in_arp_resolve ), priority=90 , match=(outport ==
> > "R1-PUB" && ip6.src == 1000::3 && is_chassis_resident("S0-P0")),
> > action=(get_nd(outport, xxreg0); next;)
> > +AT_CHECK([grep "lr_in_arp_resolve" R1flows | grep priority=90 | sed
> > 's/table=../table=??/' | sort], [0], [dnl
> > + table=??(lr_in_arp_resolve ), priority=90 , match=(outport ==
> > "R1-PUB" && ip4.src == 10.0.0.3 && is_chassis_resident("S0-P0")),
> > action=(get_arp(outport, reg0); next;)
> > + table=??(lr_in_arp_resolve ), priority=90 , match=(outport ==
> > "R1-PUB" && ip6.src == 1000::3 && is_chassis_resident("S0-P0")),
> > action=(get_nd(outport, xxreg0); next;)
> > ])
> >
> > AT_CLEANUP
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index 2cf5d5169..03e4d77e5 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -21871,7 +21871,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([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([as hv1 ovs-ofctl dump-flows br-int metadata=0x$lr0_dp_key | awk
> > '/table=29, n_packets=1, n_bytes=45/{print $7" "$8}'],[0],[dnl
> > priority=80,ip,reg15=0x3,metadata=0x3,nw_src=10.0.0.10 actions=drop
> > ])
> >
> > @@ -28888,7 +28888,7 @@ AT_CHECK([
> > 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=26 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
> > @@ -29013,7 +29013,7 @@ AT_CHECK([
> > 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=26 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
> > @@ -29510,7 +29510,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=26,
> > n_packets=1,.*
> > priority=$prio,ip,$inport.*$outport.*metadata=0x${sw_key},nw_dst=10.0.1.1
> > actions=drop" -c], [0], [dnl
> > 1
> > ])
> >
> > @@ -29529,13 +29529,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=26,
> > 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=26,
> > 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])
> > diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> > index fb54ed060..9a0cbeafe 100644
> > --- a/utilities/ovn-trace.c
> > +++ b/utilities/ovn-trace.c
> > @@ -3205,6 +3205,18 @@ trace_actions(const struct ovnact *ovnacts, size_t
> > ovnacts_len,
> > super);
> > break;
> >
> > + case OVNACT_DHCPV4_PROXY_REQ:
> > + /* TODO. */
> > + break;
> > +
> > + case OVNACT_DHCPV4_PROXY_RESP:
> > + /* TODO. */
> > + break;
> > +
> > + case OVNACT_DHCPV4_PROXY_RESP_FWD:
> > + /* TODO. */
> > + break;
> > +
> > case OVNACT_PUT_DHCPV4_OPTS:
> > execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
> > "put_dhcp_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