On Fri, Sep 8, 2023 at 12:04 AM Naveen Yerramneni <naveen.yerramn...@nutanix.com> wrote: > > Hi Team, > > Could someone please look into this ? > > Thanks, > Naveen > > > On 23-Aug-2023, at 8:06 AM, Naveen Yerramneni > > <naveen.yerramn...@nutanix.com> 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 <naveen.yerramn...@nutanix.com> > > Co-authored-by: Huzaifa Calcuttawala <huzaif...@nutanix.com> > > Signed-off-by: Huzaifa Calcuttawala <huzaif...@nutanix.com> > > CC: Mary Manohar <mary.mano...@nutanix.com> > > CC: Abhiram Sangana <sangana.abhi...@nutanix.com> > > --- > > 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 > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev