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> > > 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
