On Mon, Feb 26, 2024, 11:41 AM Naveen Yerramneni < naveen.yerramn...@nutanix.com> wrote:
> > > > On 24-Jan-2024, at 6:30 PM, Naveen Yerramneni < > naveen.yerramn...@nutanix.com> wrote: > > > > > > > >> On 24-Jan-2024, at 8:59 AM, Numan Siddique <num...@ovn.org> wrote: > >> > >> On Tue, Jan 23, 2024 at 8:02 PM Naveen Yerramneni > >> <naveen.yerramn...@nutanix.com> wrote: > >>> > >>> > >>> > >>>> On 16-Jan-2024, at 2:30 AM, Numan Siddique <num...@ovn.org> wrote: > >>>> > >>>> On Tue, Dec 12, 2023 at 1:05 PM Naveen Yerramneni > >>>> <naveen.yerramn...@nutanix.com> wrote: > >>>>> > >>>>> This patch contains changes to enable DHCP Relay Agent support for > overlay subnets. > >>>>> > >>>>> USE CASE: > >>>>> ---------- > >>>>> - Enable IP address assignment for overlay subnets from the > centralized DHCP server present in the underlay network. > >>>>> > >>>>> PREREQUISITES > >>>>> -------------- > >>>>> - Logical Router Port IP should be assigned (statically) from the > same overlay subnet which is managed by DHCP server. > >>>>> - LRP IP is used for GIADRR field when relaying the DHCP packets > and also same IP needs to be configured as default gateway for the overlay > subnet. > >>>>> - Overlay subnets managed by external DHCP server are expected to > be directly reachable from the underlay network. > >>>>> > >>>>> EXPECTED PACKET FLOW: > >>>>> ---------------------- > >>>>> Following is the expected packet flow inorder to support DHCP rleay > functionality in OVN. > >>>>> 1. DHCP client originates DHCP discovery (broadcast). > >>>>> 2. DHCP relay (running on the OVN) receives the broadcast and > forwards the packet to the DHCP server by converting it to unicast. While > forwarding the packet, it updates the GIADDR in DHCP header to its > >>>>> interface IP on which DHCP packet is received. > >>>>> 3. DHCP server uses GIADDR field to decide the IP address pool > from which IP has to be assigned and DHCP offer is sent to the same IP > (GIADDR). > >>>>> 4. DHCP relay agent forwards the offer to the client, it resets > the GIADDR field when forwarding the offer to the client. > >>>>> 5. DHCP client sends DHCP request (broadcast) packet. > >>>>> 6. DHCP relay (running on the OVN) receives the broadcast and > forwards the packet to the DHCP server by converting it to unicast. While > forwarding the packet, it updates the GIADDR in DHCP header to its > >>>>> interface IP on which DHCP packet is received. > >>>>> 7. DHCP Server sends the ACK packet. > >>>>> 8. DHCP relay agent forwards the ACK packet to the client, it > resets the GIADDR field when forwarding the ACK to the client. > >>>>> 9. All the future renew/release packets are directly exchanged > between DHCP client and DHCP server. > >>>>> > >>>>> OVN DHCP RELAY PACKET FLOW: > >>>>> ---------------------------- > >>>>> To add DHCP Relay support on OVN, we need to replicate all the > behavior described above using distributed logical switch and logical > router. > >>>>> At, highlevel packet flow is distributed among Logical Switch and > Logical Router on source node (where VM is deployed) and redirect > chassis(RC) node. > >>>>> 1. Request packet gets processed on the source node where VM is > deployed and relays the packet to DHCP server. > >>>>> 2. Response packet is first processed on RC node (which first > recieves the packet from underlay network). RC node forwards the packet to > the right node by filling in the dest MAC and IP. > >>>>> > >>>>> OVN Packet flow with DHCP relay is explained below. > >>>>> 1. DHCP client (VM) sends the DHCP discover packet (broadcast). > >>>>> 2. Logical switch converts the packet to L2 unicast by setting > the destination MAC to LRP's MAC > >>>>> 3. Logical Router receives the packet and redirects it to the OVN > controller. > >>>>> 4. OVN controller updates the required information(GIADDR) in the > DHCP payload after doing the required checks. If any check fails, packet is > dropped. > >>>>> 5. Logical Router converts the packet to L3 unicast and forwards > it to the server. This packets gets routed like any other packet (via RC > node). > >>>>> 6. Server replies with DHCP offer. > >>>>> 7. RC node processes the DHCP offer and forwards it to the OVN > controller. > >>>>> 8. OVN controller does sanity checks and updates the destination > MAC (available in DHCP header), destination IP (available in DHCP header), > resets GIADDR and reinjects the packet to datapath. > >>>>> If any check fails, packet is dropped. > >>>>> 9. Logical router updates the source IP and port and forwards the > packet to logical switch. > >>>>> 10. Logical switch delivers the packet to the DHCP client. > >>>>> 11. Similar steps are performed for Request and Ack packets. > >>>>> 12. All the future renew/release packets are directly exchanged > between DHCP client and DHCP server > >>>>> > >>>>> NEW OVN ACTIONS > >>>>> --------------- > >>>>> > >>>>> 1. dhcp_relay_req(<relay-ip>, <server-ip>) > >>>>> - This action executes on the source node on which the DHCP > request originated. > >>>>> - This action relays the DHCP request coming from client to > the server. Relay-ip is used to update GIADDR in the DHCP header. > >>>>> 2. dhcp_relay_resp_fwd(<relay-ip>, <server-ip>) > >>>>> - This action executes on the first node (RC node) which > processes the DHCP response from the server. > >>>>> - This action updates the destination MAC and destination IP > so that the response can be forwarded to the appropriate node from which > request was originated. > >>>>> - Relay-ip, server-ip are used to validate GIADDR and SERVER > ID in the DHCP payload. > >>>>> > >>>>> FLOWS > >>>>> ----- > >>>>> Following are the flows required for one overlay 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_RELAY_REQ */) > >>>>> 2. 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_relay_req(<lrp_ip>,<dhcp_server_ip>);ip4.src=<lrp_ip>;ip4.dst=<dhcp_server_ip>;udp.src=67;next; > /* DHCP_RELAY_REQ */) > >>>>> 3. 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_RELAY_RESP */) > >>>>> 4. table=17(lr_in_dhcp_relay_resp_fwd), priority=110 , > match=(ip4.src == <dhcp_server_ip> && ip4.dst == <lrp_ip> && udp.src == 67 > && udp.dst == 67), > action=(dhcp_relay_resp_fwd(<lrp_ip>,<dhcp_server_ip>);ip4.src=<lrp_ip>;udp.dst=68;outport=<lrp_port>;output; > /* DHCP_RELAY_RESP */) > >>>>> > >>>>> NEW PIPELINE STAGES > >>>>> ------------------- > >>>>> Following stage is added for DHCP relay feature. Some of the flows > are fitted into the existing pipeline tages. > >>>>> 1. lr_in_dhcp_relay_resp_fwd > >>>>> - Forward teh DHCP response to the appropriate node > >>>>> > >>>>> NB SCHEMA CHANGES > >>>>> ---------------- > >>>>> 1. New DHCP_Relay table > >>>>> "DHCP_Relay": { > >>>>> "columns": { > >>>>> "name": {"type": "string"}, > >>>>> "servers": {"type": {"key": "string", > >>>>> "min": 0, > >>>>> "max": 1}}, > >>>>> "external_ids": { > >>>>> "type": {"key": "string", "value": "string", > >>>>> "min": 0, "max": "unlimited"}}}, > >>>>> "isRoot": true}, > >>>>> 2. New column to Logical_Router_Port table > >>>>> "dhcp_relay": {"type": {"key": {"type": "uuid", > >>>>> "refTable": "DHCP_Relay", > >>>>> "refType": "weak"}, > >>>>> "min": 0, > >>>>> "max": 1}}, > >>>>> 3. New column to Logical_Switch_table > >>>>> "dhcp_relay_port": {"type": {"key": {"type": "uuid", > >>>>> "refTable": > "Logical_Router_Port", > >>>>> "refType": "weak"}, > >>>>> "min": 0, > >>>>> "max": 1}}}, > >>>>> > >>>>> Commands to enable the feature: > >>>>> ------------------------------ > >>>>> - ovn-nbctl create DHCP_Relay servers=<ip> > >>>>> - ovn-nbctl set Logical_Router_port <lrp_uuid> > dhcp_relay=<dhcp_relay_uuid> > >>>>> - ovn-nbctl set Logical_Switch <ls_uuid> > dhcp_relay_port=<lrp_uuid> > >>>>> > >>>>> Example: > >>>>> ------- > >>>>> ovn-nbctl ls-add sw1 > >>>>> ovn-nbctl lsp-add sw1 sw1-port1 > >>>>> ovn-nbctl lsp-set-addresses sw1-port1 <MAC> #Only MAC address has > to be specified when logical ports are created. > >>>>> ovn-nbctl lr-add lr1 > >>>>> ovn-nbctl lrp-add lr1 lr1-port1 <MAC> <GATEWAY_IP/Prefix> #GATEWAY > IP is set in GIADDR field when relaying the DHCP requests to server. > >>>>> ovn-nbctl lsp-add sw1 lr1-attachment > >>>>> ovn-nbctl lsp-set-type lr1-attachment router > >>>>> ovn-nbctl lsp-set-addresses lr1-attachment <MAC> > >>>>> ovn-nbctl lsp-set-options lr1-attachment router-port=lr1-port1 > >>>>> ovn-nbctl create DHCP_Relay servers=<DHCP_SERVER_IP> > >>>>> ovn-nbctl set Logical_Router_port <lrp_uuid> > dhcp_relay=<relay_uuid> > >>>>> ovn-nbctl set Logical_Switch <ls_uuid> dhcp_relay_port=<lrp_uuid> > >>>>> > >>>>> Limitations: > >>>>> ------------ > >>>>> - All OVN features that needs IP address to be configured on > logical port (like proxy arp, etc) will not be supported for overlay > subnets on which DHCP relay is enabled. > >>>>> > >>>>> References: > >>>>> ---------- > >>>>> - rfc1541, rfc1542, rfc2131 > >>>>> > >>>>> Signed-off-by: Naveen Yerramneni <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> > >>>> > >>>> Hi Naveen, > >>>> > >>>> Thanks for the patch. Sorry for the delayed response. > >>>> > >>>> I've a few comments. > >>>> > >>>> 1. Regarding the newly added Table - DHCP_Relay in NB DB and the > >>>> newly added columns in Logical_Switch and > >>>> Logical_Router table. > >>>> > >>>> I don't think there is a need to add the new table DHCP_Relay > >>>> since it only stores the dhcp relay agent server ip. > >>>> Also it could complicate the northd incremental processing. > >>>> > >>>> If for example we have below logical switches and router > >>>> > >>>> ovn-nbctl lr-add R1 > >>>> ovn-nbctl ls-add sw0 > >>>> ovn-nbctl ls-add sw1 > >>>> ovn-nbctl ls-add sw-ext > >>>> ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24 > >>>> ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24 > >>>> ovn-nbctl lrp-add R1 rp-ext 00:00:02:01:02:03 172.16.1.254/24 > >>>> > >>>> ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \ > >>>> type=router options:router-port=rp-sw0 \ > >>>> -- lsp-set-addresses sw0-rp router > >>>> > >>>> ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \ > >>>> type=router options:router-port=rp-sw1 \ > >>>> -- lsp-set-addresses sw1-rp router > >>>> > >>>> I'd suggest doing something like below to enable this feature. > >>>> > >>>> ovn-nbctl set Logical_Switch_Port sw0-rp options:dhcp_relay=true > >>>> ovn-nbctl set Logical_Switch_Port sw1-rp options:dhcp_relay=true > >>>> > >>>> (Make sure that only one logical switch port of type router can > >>>> have this flag - dhcp_relay set > >>>> for a given logical switch and document this limitation.) > >>> > >>> Ack. This suggestion looks good. > >>> > >>>> ovn-nbctl set Logical_Router_port rp-sw0 > options:dhcp_relay_ip=172.16.1.1 > >>>> ovn-nbctl set Logical_Router_port rp-sw1 > options:dhcp_relay_ip=172.16.1.1 > >>>> > >>>> Let me know if there are any limitations with this. > >>> > >>> The reason why I added new table is , it would be useful in future if > we add > >>> additional options (like setting hop count in DHCP header, etc) to > DHCP relay > >>> functionality. What do you recommend if we have to add more options > >>> In future ? > >> > >> I see. If there is a possibility of adding more options, then having > >> a separate table makes sense. > >> I'd suggest to add the options column to the DHCP_Relay table even if > >> this patch presently is not using > >> any. This would help in upgrades. > >> > >> But I don't think there is a need to add a new column in the logical > >> switch port table to enable dhcp realy. > >> > >> Thanks > >> Numan > > > > Sure, I will add options column to DHCP_Relay column. > > I will use options:dhcp_relay for LSP instead of new column as you > suggested. > > > > Thanks, > > Naveen > > > > Hi Numan, > > I started working on your comments. > > Regd options:dhcp_relay for LSP: Since DHCP relay is applicable at logical > switch > level (for entire subnet). I am thinking what if we add options:dhcp_relay > with value > as string (lsp name of port type router) to Logical Switch table ? > Please let me know your thoughts on this. Sounds good to me. > > > > > >> > >>> > >>> > >>> > >>>> 2. Regarding the newly added actions - dhcp_relay_req() and > >>>> dhcp_relay_resp_fwd(). > >>>> Both of these actions are encoded as OVS controller action with > >>>> pause enabled. > >>>> Which means ovs-vswitchd has to freeze the flow translation and > >>>> resume the flow translation > >>>> once the ovn-controller resumes it. But the functions > >>>> pinctrl_handle_dhcp_relay_req() > >>>> and pinctrl_handle_dhcp_relay_resp_fwd() do not resume the packet > >>>> if the packet > >>>> has some errors. This is wrong. Otherwise vswitchd will never > thaw the > >>>> frozen translation. > >>>> > >>>> You can see the existing OVN actions - put_dhcp_opts() and few > others which > >>>> use controller action with pause. In such actions, the result of > >>>> these actions > >>>> are stored in a register bit (i.e if put_dhcp_opts() was successful > or not) > >>>> and in the next stage we take a decision based on the result. > >>>> > >>>> For the action dhcp_relay_req(relay_ip, server_ip), I don't > >>>> think you should use the pause flag. > >>>> Also in this action the argument server_ip is never used in the > >>>> function pinctrl_handle_dhcp_relay_req() > >>>> other than to just log. > >>>> > >>>> I'd suggest you do something like this: > >>>> > >>>> table=3 (lr_in_ip_input ), priority=110 , match=(inport == > >>>> "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src > >>>> == 68 && udp.dst == 67), > >>>> action=(dhcp_relay_req { ip4.src = 192.168.1.1; ip4.dst = > >>>> 172.16.1.1; udp.src = 67; dhcp_header.giaddr = <relay_ip>; > >>>> next(pipeline=ingress,table=S_ROUTER_IN_UNSNAT); /* DHCP_RELAY_REQ */ > >>>> } > >>>> > >>>> dhcp_relay_req action would get translated into a controller > >>>> action with pause=false and all the inner actions of this are encoded > >>>> as > >>>> normal actions and stored in the userdata of controller action. > >>>> Please see icmp4_error {} as an example. > >>>> Add a new OVN field 'dhcp_header.giaddr' which gets translated as > >>>> controller action with pause flag set. > >>>> Please see the existing OVN field - icmp4.frag_mtu as an example > >>>> and see this commit for reference [1] > >>>> When encoding this new OVN field, store the relay_ip in the > >>>> userdata buffer and in pinctrl.c > >>>> get the relay_ip value and store it in the dhcp header field. > >>>> > >>>> > >>>> For the action dhcp_relay_resp_fwd, I'd suggest something like > below: > >>>> > >>>> table=17 (lr_in_dhcp_relay_resp_chk), priority=110 , > >>>> match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == > >>>> 67 && udp.dst == 67), > >>>> action=(reg0[0] = dhcp_relay_resp_chk(dhcp_header.giaddr == > >>>> <relay_ip>); next;) > >>>> table=17 (lr_in_dhcp_relay_resp), priority=110 , match=(ip4.src > >>>> == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == > >>>> 67 && reg0[0] == 1), > >>>> action=(ip4.src = 192.168.1.1; udp.dst = 68; outport = "lrp1"; > >>>> output; /* DHCP_RELAY_RESP */) > >>>> > >>>> I used reg0[0] as an example. You may need to check the free > >>>> register bit and use it. > >>>> > >>>> You need to encode dhcp_relay_resp_chk as controller action with > >>>> pause=true, and store the relay_ip in the userdata buffer. > >>>> And in pinctrl.c check that 'dhcp_header.giaddr == relay_ip' > >>>> or not. If so, set the result register bit to 1, else to 0. > >>>> > >>>> Let me know if you've any questions. > >>>> > >>> > >>> Ack. Thanks for the suggestions and detailed explanation. > >>> Before implementation I had referred to icmp4_error and native > dhcp_server flows > >>> but I had slight misunderstanding about pause flag. > >>> > > > Regd dhcp_relay_req: I think it might be better to implement two stage > processing > for dhcp_relay_req similar to dhcp_relay_resp (something like below). > This will avoid > multiple OVN actions/fields if we update additional fields (like hop count > in DHCP header) in future. > > table=3 (lr_in_ip_input ), priority=110 , match=(inport == "lrp1" && > ip4.src == 0.0.0.0 && > ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), > action=(regx[y] = dhcp_relay_req_chk(192.168.1.1,172.16.1.1);next; /* > DHCP_RELAY_REQ */) > > table=4 (lr_in_ip_dhcp_req ), priority=110 , match=(inport == "lrp1" > && ip4.src == 0.0.0.0 && > ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && regx[y]), > action=(ip4.src=192.168.1.1;ip4.dst=172.16.1.1; > udp.src=67;next; /* DHCP_RELAY_REQ */) > > table=4 (lr_in_ip_dhcp_req ), priority=1 , match=(inport == "lrp1" && > ip4.src == 0.0.0.0 && > ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && regx[y] == > 0), > action=drop; > > Please let me know if you are fine with this approach. > Makes sense to have 2 stages if the action dhcp_relay_req_chk() in pinctrl.c would ignore the packet if some errors. Looking forward to the patches. Thanks Numan > > > >>> > >>>> 3. The newly added functions in pinctrl.c have a lot of repetitive > >>>> code and it is very much similar to existing > >>>> pinctrl_handle_put_dhcp_opts() > >>>> Please see if the duplicate code can be avoided. > >>> > >>> Ack. > >>> > >>> > >>> > >>>> [1] - > https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_ovn-2Dorg_ovn_commit_3d9fec3fd5992e1201b4d4fdf43f1f397e8d5ea1&d=DwIFaQ&c=s883GpUCOChKOHiocYtGcg&r=2PQjSDR7A28z1kXE1ptSm6X36oL_nCq1XxeEt7FkLmA&m=jUP6tr4FN6iSRj6v8rdyetsvEpT13QUHVMbw__3u6Sm7qAhyuu9tBdezdVmkqt0p&s=xAleLPNTzueIGuScqWZRp7ppL2D7bbjqLZc6q4xk3Rg&e= > >>>> > >>>> Thanks > >>>> Numan > >>>> > >>>>> --- > >>>>> controller/pinctrl.c | 441 > ++++++++++++++++++++++++++++++++++++++++++ > >>>>> include/ovn/actions.h | 26 +++ > >>>>> lib/actions.c | 117 +++++++++++ > >>>>> lib/ovn-l7.h | 1 + > >>>>> northd/northd.c | 177 ++++++++++++++++- > >>>>> ovn-nb.ovsschema | 25 ++- > >>>>> ovn-nb.xml | 28 +++ > >>>>> tests/atlocal.in | 3 + > >>>>> tests/ovn-northd.at | 41 +++- > >>>>> tests/ovn.at | 12 +- > >>>>> tests/system-ovn.at | 150 ++++++++++++++ > >>>>> utilities/ovn-trace.c | 28 +++ > >>>>> 12 files changed, 1032 insertions(+), 17 deletions(-) > >>>>> > >>>>> diff --git a/controller/pinctrl.c b/controller/pinctrl.c > >>>>> index 5a35d56f6..45240f01d 100644 > >>>>> --- a/controller/pinctrl.c > >>>>> +++ b/controller/pinctrl.c > >>>>> @@ -1897,6 +1897,437 @@ is_dhcp_flags_broadcast(ovs_be16 flags) > >>>>> return flags & htons(DHCP_BROADCAST_FLAG); > >>>>> } > >>>>> > >>>>> +static const char *dhcp_msg_str[] = { > >>>>> +[0] = "INVALID", > >>>>> +[DHCP_MSG_DISCOVER] = "DISCOVER", > >>>>> +[DHCP_MSG_OFFER] = "OFFER", > >>>>> +[DHCP_MSG_REQUEST] = "REQUEST", > >>>>> +[OVN_DHCP_MSG_DECLINE] = "DECLINE", > >>>>> +[DHCP_MSG_ACK] = "ACK", > >>>>> +[DHCP_MSG_NAK] = "NAK", > >>>>> +[OVN_DHCP_MSG_RELEASE] = "RELEASE", > >>>>> +[OVN_DHCP_MSG_INFORM] = "INFORM" > >>>>> +}; > >>>>> + > >>>>> +static bool > >>>>> +dhcp_relay_is_msg_type_supported(uint8_t msg_type) > >>>>> +{ > >>>>> + return (msg_type >= DHCP_MSG_DISCOVER && msg_type <= > OVN_DHCP_MSG_RELEASE); > >>>>> +} > >>>>> + > >>>>> +static const char *dhcp_msg_str_get(uint8_t msg_type) > >>>>> +{ > >>>>> + if (!dhcp_relay_is_msg_type_supported(msg_type)) { > >>>>> + return "INVALID"; > >>>>> + } > >>>>> + return dhcp_msg_str[msg_type]; > >>>>> +} > >>>>> + > >>>>> +/* Called with in the pinctrl_handler thread context. */ > >>>>> +static void > >>>>> +pinctrl_handle_dhcp_relay_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 *relay_ip = ofpbuf_try_pull(userdata, sizeof > *relay_ip); > >>>>> + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof > *server_ip); > >>>>> + if (!relay_ip || !server_ip) { > >>>>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, > 5); > >>>>> + VLOG_WARN_RL(&rl, "DHCP_RELAY_REQ: relay 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_RELAY_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_RELAY_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_RELAY_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_RELAY_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_RELAY_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_RELAY_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; > >>>>> + ovs_be32 request_ip = in_dhcp_data->ciaddr; > >>>>> + while (in_dhcp_ptr < end) { > >>>>> + const struct dhcp_opt_header *in_dhcp_opt = > >>>>> + (const struct dhcp_opt_header *) in_dhcp_ptr; > >>>>> + if (in_dhcp_opt->code == DHCP_OPT_END) { > >>>>> + break; > >>>>> + } > >>>>> + if (in_dhcp_opt->code == DHCP_OPT_PAD) { > >>>>> + in_dhcp_ptr += 1; > >>>>> + continue; > >>>>> + } > >>>>> + in_dhcp_ptr += sizeof *in_dhcp_opt; > >>>>> + if (in_dhcp_ptr > end) { > >>>>> + break; > >>>>> + } > >>>>> + in_dhcp_ptr += in_dhcp_opt->len; > >>>>> + if (in_dhcp_ptr > end) { > >>>>> + break; > >>>>> + } > >>>>> + > >>>>> + switch (in_dhcp_opt->code) { > >>>>> + case DHCP_OPT_MSG_TYPE: > >>>>> + if (in_dhcp_opt->len == 1) { > >>>>> + in_dhcp_msg_type = DHCP_OPT_PAYLOAD(in_dhcp_opt); > >>>>> + } > >>>>> + break; > >>>>> + case DHCP_OPT_REQ_IP: > >>>>> + if (in_dhcp_opt->len == 4) { > >>>>> + request_ip = > get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt)); > >>>>> + } > >>>>> + break; > >>>>> + /* Server Identifier */ > >>>>> + case OVN_DHCP_OPT_CODE_SERVER_ID: > >>>>> + 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_RELAY_REQ: missing message type"); > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + /* Relay the DHCP request packet */ > >>>>> + uint16_t new_l4_size = in_l4_size; > >>>>> + size_t new_packet_size = pkt_in->l4_ofs + new_l4_size; > >>>>> + > >>>>> + struct dp_packet pkt_out; > >>>>> + dp_packet_init(&pkt_out, new_packet_size); > >>>>> + dp_packet_clear(&pkt_out); > >>>>> + dp_packet_prealloc_tailroom(&pkt_out, new_packet_size); > >>>>> + pkt_out_ptr = &pkt_out; > >>>>> + > >>>>> + /* Copy the L2 and L3 headers from the pkt_in as they would > remain same*/ > >>>>> + dp_packet_put( > >>>>> + &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), > pkt_in->l4_ofs); > >>>>> + > >>>>> + pkt_out.l2_5_ofs = pkt_in->l2_5_ofs; > >>>>> + pkt_out.l2_pad_size = pkt_in->l2_pad_size; > >>>>> + pkt_out.l3_ofs = pkt_in->l3_ofs; > >>>>> + pkt_out.l4_ofs = pkt_in->l4_ofs; > >>>>> + > >>>>> + struct 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 = *relay_ip; > >>>>> + if (udp->udp_csum) { > >>>>> + udp->udp_csum = recalc_csum32(udp->udp_csum, > >>>>> + 0, dhcp_data->giaddr); > >>>>> + } > >>>>> + pin->packet = dp_packet_data(&pkt_out); > >>>>> + pin->packet_len = dp_packet_size(&pkt_out); > >>>>> + > >>>>> + /* Log the DHCP message. */ > >>>>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); > >>>>> + const struct eth_header *l2 = dp_packet_eth(&pkt_out); > >>>>> + VLOG_INFO_RL(&rl, "DHCP_RELAY_REQ:: MSG_TYPE:%s > MAC:"ETH_ADDR_FMT > >>>>> + " XID:%u" > >>>>> + " REQ_IP:"IP_FMT > >>>>> + " GIADDR:"IP_FMT > >>>>> + " SERVER_ADDR:"IP_FMT, > >>>>> + dhcp_msg_str_get(*in_dhcp_msg_type), > >>>>> + ETH_ADDR_BYTES_ARGS(dhcp_data->chaddr), > ntohl(dhcp_data->xid), > >>>>> + IP_ARGS(request_ip), IP_ARGS(dhcp_data->giaddr), > >>>>> + IP_ARGS(*server_ip)); > >>>>> + queue_msg(swconn, ofputil_encode_resume(pin, continuation, > proto)); > >>>>> + if (pkt_out_ptr) { > >>>>> + dp_packet_uninit(pkt_out_ptr); > >>>>> + } > >>>>> +} > >>>>> + > >>>>> +/* Called with in the pinctrl_handler thread context. */ > >>>>> +static void > >>>>> +pinctrl_handle_dhcp_relay_resp_fwd( > >>>>> + 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 *relay_ip = ofpbuf_try_pull(userdata, sizeof > *relay_ip); > >>>>> + ovs_be32 *server_ip = ofpbuf_try_pull(userdata, sizeof > *server_ip); > >>>>> + if (!relay_ip || !server_ip) { > >>>>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, > 5); > >>>>> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: relay 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_RELAY_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_RELAY_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_RELAY_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_RELAY_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_RELAY_RESP_FWD: giaddr is " > >>>>> + "not set in request"); > >>>>> + return; > >>>>> + } > >>>>> + ovs_be32 giaddr = in_dhcp_data->giaddr; > >>>>> + > >>>>> + ovs_be32 *server_id_ptr = NULL; > >>>>> + ovs_be32 lease_time = 0; > >>>>> + 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; > >>>>> + /* Server Identifier */ > >>>>> + case OVN_DHCP_OPT_CODE_SERVER_ID: > >>>>> + 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_RELAY_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_RELAY_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_RELAY_RESP: server identifier > mismatch"); > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + if (giaddr != *relay_ip) { > >>>>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, > 5); > >>>>> + VLOG_WARN_RL(&rl, "DHCP_RELAY_RESP: giaddr mismatch"); > >>>>> + 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); > >>>>> + } > >>>>> + /* Reset giaddr */ > >>>>> + dhcp_data->giaddr = htonl(0x0); > >>>>> + if (udp->udp_csum) { > >>>>> + udp->udp_csum = recalc_csum32(udp->udp_csum, > >>>>> + giaddr, 0); > >>>>> + } > >>>>> + pin->packet = dp_packet_data(&pkt_out); > >>>>> + pin->packet_len = dp_packet_size(&pkt_out); > >>>>> + > >>>>> + /* Log the DHCP message. */ > >>>>> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); > >>>>> + const struct eth_header *l2 = dp_packet_eth(&pkt_out); > >>>>> + VLOG_INFO_RL(&rl, "DHCP_RELAY_RESP_FWD:: MSG_TYPE:%s > MAC:"ETH_ADDR_FMT > >>>>> + " XID:%u" > >>>>> + " YIADDR:"IP_FMT > >>>>> + " GIADDR:"IP_FMT > >>>>> + " SERVER_ADDR:"IP_FMT, > >>>>> + dhcp_msg_str_get(*in_dhcp_msg_type), > >>>>> + ETH_ADDR_BYTES_ARGS(dhcp_data->chaddr), > ntohl(dhcp_data->xid), > >>>>> + IP_ARGS(dhcp_data->yiaddr), > >>>>> + IP_ARGS(giaddr), IP_ARGS(*server_id_ptr)); > >>>>> + 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( > >>>>> @@ -3203,6 +3634,16 @@ process_packet_in(struct rconn *swconn, const > struct ofp_header *msg) > >>>>> ovs_mutex_unlock(&pinctrl_mutex); > >>>>> break; > >>>>> > >>>>> + case ACTION_OPCODE_DHCP_RELAY_REQ: > >>>>> + pinctrl_handle_dhcp_relay_req(swconn, &packet, &pin, > >>>>> + &userdata, &continuation); > >>>>> + break; > >>>>> + > >>>>> + case ACTION_OPCODE_DHCP_RELAY_RESP_FWD: > >>>>> + pinctrl_handle_dhcp_relay_resp_fwd(swconn, &packet, &pin, > >>>>> + &userdata, &continuation); > >>>>> + break; > >>>>> + > >>>>> case ACTION_OPCODE_PUT_DHCP_OPTS: > >>>>> pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers, > >>>>> &userdata, &continuation); > >>>>> diff --git a/include/ovn/actions.h b/include/ovn/actions.h > >>>>> index 49cfe0624..47d41b90f 100644 > >>>>> --- a/include/ovn/actions.h > >>>>> +++ b/include/ovn/actions.h > >>>>> @@ -95,6 +95,8 @@ struct collector_set_ids; > >>>>> OVNACT(LOOKUP_ND_IP, ovnact_lookup_mac_bind_ip) \ > >>>>> OVNACT(PUT_DHCPV4_OPTS, ovnact_put_opts) \ > >>>>> OVNACT(PUT_DHCPV6_OPTS, ovnact_put_opts) \ > >>>>> + OVNACT(DHCPV4_RELAY_REQ, ovnact_dhcp_relay) \ > >>>>> + OVNACT(DHCPV4_RELAY_RESP_FWD, ovnact_dhcp_relay) \ > >>>>> OVNACT(SET_QUEUE, ovnact_set_queue) \ > >>>>> OVNACT(DNS_LOOKUP, ovnact_result) \ > >>>>> OVNACT(LOG, ovnact_log) \ > >>>>> @@ -387,6 +389,14 @@ struct ovnact_put_opts { > >>>>> size_t n_options; > >>>>> }; > >>>>> > >>>>> +/* OVNACT_DHCP_RELAY. */ > >>>>> +struct ovnact_dhcp_relay { > >>>>> + struct ovnact ovnact; > >>>>> + int family; > >>>>> + ovs_be32 relay_ipv4; > >>>>> + ovs_be32 server_ipv4; > >>>>> +}; > >>>>> + > >>>>> /* Valid arguments to SET_QUEUE action. > >>>>> * > >>>>> * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues > should > >>>>> @@ -750,6 +760,22 @@ enum action_opcode { > >>>>> > >>>>> /* multicast group split buffer action. */ > >>>>> ACTION_OPCODE_MG_SPLIT_BUF, > >>>>> + > >>>>> + /* "dhcp_relay_req(relay_ip, server_ip)". > >>>>> + * > >>>>> + * Arguments follow the action_header, in this format: > >>>>> + * - The 32-bit DHCP relay IP. > >>>>> + * - The 32-bit DHCP server IP. > >>>>> + */ > >>>>> + ACTION_OPCODE_DHCP_RELAY_REQ, > >>>>> + > >>>>> + /* "dhcp_relay_resp_fwd(relay_ip, server_ip)". > >>>>> + * > >>>>> + * Arguments follow the action_header, in this format: > >>>>> + * - The 32-bit DHCP relay IP. > >>>>> + * - The 32-bit DHCP server IP. > >>>>> + */ > >>>>> + ACTION_OPCODE_DHCP_RELAY_RESP_FWD, > >>>>> }; > >>>>> > >>>>> /* Header. */ > >>>>> diff --git a/lib/actions.c b/lib/actions.c > >>>>> index a73fe1a1e..69df428c6 100644 > >>>>> --- a/lib/actions.c > >>>>> +++ b/lib/actions.c > >>>>> @@ -2629,6 +2629,118 @@ ovnact_controller_event_free(struct > ovnact_controller_event *event) > >>>>> free_gen_options(event->options, event->n_options); > >>>>> } > >>>>> > >>>>> +static void > >>>>> +format_DHCPV4_RELAY_REQ(const struct ovnact_dhcp_relay *dhcp_relay, > >>>>> + struct ds *s) > >>>>> +{ > >>>>> + ds_put_format(s, "dhcp_relay_req("IP_FMT","IP_FMT");", > >>>>> + IP_ARGS(dhcp_relay->relay_ipv4), > >>>>> + IP_ARGS(dhcp_relay->server_ipv4)); > >>>>> +} > >>>>> + > >>>>> +static void > >>>>> +parse_dhcp_relay_req(struct action_context *ctx, > >>>>> + struct ovnact_dhcp_relay *dhcp_relay) > >>>>> +{ > >>>>> + /* Skip dhcp_relay_req( */ > >>>>> + lexer_force_match(ctx->lexer, LEX_T_LPAREN); > >>>>> + > >>>>> + /* Parse relay ip and server ip. */ > >>>>> + if (ctx->lexer->token.format == LEX_F_IPV4) { > >>>>> + dhcp_relay->family = AF_INET; > >>>>> + dhcp_relay->relay_ipv4 = ctx->lexer->token.value.ipv4; > >>>>> + lexer_get(ctx->lexer); > >>>>> + lexer_match(ctx->lexer, LEX_T_COMMA); > >>>>> + if (ctx->lexer->token.format == LEX_F_IPV4) { > >>>>> + dhcp_relay->family = AF_INET; > >>>>> + dhcp_relay->server_ipv4 = ctx->lexer->token.value.ipv4; > >>>>> + lexer_get(ctx->lexer); > >>>>> + } else { > >>>>> + lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp > server ip"); > >>>>> + return; > >>>>> + } > >>>>> + } else { > >>>>> + lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp relay > " > >>>>> + "and server ips"); > >>>>> + return; > >>>>> + } > >>>>> + lexer_force_match(ctx->lexer, LEX_T_RPAREN); > >>>>> +} > >>>>> + > >>>>> +static void > >>>>> +encode_DHCPV4_RELAY_REQ(const struct ovnact_dhcp_relay *dhcp_relay, > >>>>> + const struct ovnact_encode_params *ep, > >>>>> + struct ofpbuf *ofpacts) > >>>>> +{ > >>>>> + size_t oc_offset = > encode_start_controller_op(ACTION_OPCODE_DHCP_RELAY_REQ, > >>>>> + true, > ep->ctrl_meter_id, > >>>>> + ofpacts); > >>>>> + ofpbuf_put(ofpacts, &dhcp_relay->relay_ipv4, > >>>>> + sizeof(dhcp_relay->relay_ipv4)); > >>>>> + ofpbuf_put(ofpacts, &dhcp_relay->server_ipv4, > >>>>> + sizeof(dhcp_relay->server_ipv4)); > >>>>> + encode_finish_controller_op(oc_offset, ofpacts); > >>>>> +} > >>>>> + > >>>>> +static void > >>>>> +format_DHCPV4_RELAY_RESP_FWD(const struct ovnact_dhcp_relay > *dhcp_relay, > >>>>> + struct ds *s) > >>>>> +{ > >>>>> + ds_put_format(s, "dhcp_relay_resp("IP_FMT","IP_FMT");", > >>>>> + IP_ARGS(dhcp_relay->relay_ipv4), > >>>>> + IP_ARGS(dhcp_relay->server_ipv4)); > >>>>> +} > >>>>> + > >>>>> +static void > >>>>> +parse_dhcp_relay_resp_fwd(struct action_context *ctx, > >>>>> + struct ovnact_dhcp_relay *dhcp_relay) > >>>>> +{ > >>>>> + /* Skip dhcp_relay_resp( */ > >>>>> + lexer_force_match(ctx->lexer, LEX_T_LPAREN); > >>>>> + > >>>>> + /* Parse relay ip and server ip. */ > >>>>> + if (ctx->lexer->token.format == LEX_F_IPV4) { > >>>>> + dhcp_relay->family = AF_INET; > >>>>> + dhcp_relay->relay_ipv4 = ctx->lexer->token.value.ipv4; > >>>>> + lexer_get(ctx->lexer); > >>>>> + lexer_match(ctx->lexer, LEX_T_COMMA); > >>>>> + if (ctx->lexer->token.format == LEX_F_IPV4) { > >>>>> + dhcp_relay->family = AF_INET; > >>>>> + dhcp_relay->server_ipv4 = ctx->lexer->token.value.ipv4; > >>>>> + lexer_get(ctx->lexer); > >>>>> + } else { > >>>>> + lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp > server ip"); > >>>>> + return; > >>>>> + } > >>>>> + } else { > >>>>> + lexer_syntax_error(ctx->lexer, "expecting IPv4 dhcp relay > and " > >>>>> + "server ips"); > >>>>> + return; > >>>>> + } > >>>>> + lexer_force_match(ctx->lexer, LEX_T_RPAREN); > >>>>> +} > >>>>> + > >>>>> +static void > >>>>> +encode_DHCPV4_RELAY_RESP_FWD(const struct ovnact_dhcp_relay > *dhcp_relay, > >>>>> + const struct ovnact_encode_params *ep, > >>>>> + struct ofpbuf *ofpacts) > >>>>> +{ > >>>>> + size_t oc_offset = encode_start_controller_op( > >>>>> + ACTION_OPCODE_DHCP_RELAY_RESP_FWD, > >>>>> + true, ep->ctrl_meter_id, > >>>>> + ofpacts); > >>>>> + ofpbuf_put(ofpacts, &dhcp_relay->relay_ipv4, > >>>>> + sizeof(dhcp_relay->relay_ipv4)); > >>>>> + ofpbuf_put(ofpacts, &dhcp_relay->server_ipv4, > >>>>> + sizeof(dhcp_relay->server_ipv4)); > >>>>> + encode_finish_controller_op(oc_offset, ofpacts); > >>>>> +} > >>>>> + > >>>>> +static void ovnact_dhcp_relay_free( > >>>>> + struct ovnact_dhcp_relay *dhcp_relay OVS_UNUSED) > >>>>> +{ > >>>>> +} > >>>>> + > >>>>> static void > >>>>> parse_put_opts(struct action_context *ctx, const struct expr_field > *dst, > >>>>> struct ovnact_put_opts *po, const struct hmap *gen_opts, > >>>>> @@ -5451,6 +5563,11 @@ parse_action(struct action_context *ctx) > >>>>> parse_sample(ctx); > >>>>> } else if (lexer_match_id(ctx->lexer, "mac_cache_use")) { > >>>>> ovnact_put_MAC_CACHE_USE(ctx->ovnacts); > >>>>> + } else if (lexer_match_id(ctx->lexer, "dhcp_relay_req")) { > >>>>> + parse_dhcp_relay_req(ctx, > ovnact_put_DHCPV4_RELAY_REQ(ctx->ovnacts)); > >>>>> + } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_fwd")) { > >>>>> + parse_dhcp_relay_resp_fwd(ctx, > >>>>> + ovnact_put_DHCPV4_RELAY_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 ad514a922..e08581123 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 07dffb15a..7ac831fae 100644 > >>>>> --- a/northd/northd.c > >>>>> +++ b/northd/northd.c > >>>>> @@ -181,11 +181,13 @@ 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_RELAY_RESP_FWD, 17, > \ > >>>>> + "lr_in_dhcp_relay_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, > \ > >>>>> @@ -9610,6 +9612,80 @@ build_dhcpv6_options_flows(struct ovn_port > *op, > >>>>> ds_destroy(&match); > >>>>> } > >>>>> > >>>>> +static void > >>>>> +build_lswitch_dhcp_relay_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_relay_port) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + struct ds match = DS_EMPTY_INITIALIZER; > >>>>> + struct ds action = DS_EMPTY_INITIALIZER; > >>>>> + struct nbrec_logical_router_port *lrp = > op->od->nbs->dhcp_relay_port; > >>>>> + struct ovn_port *rp = ovn_port_find(lr_ports, lrp->name); > >>>>> + > >>>>> + if (!rp || !rp->nbrp || !rp->nbrp->dhcp_relay) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + struct ovn_port *sp = NULL; > >>>>> + struct nbrec_dhcp_relay *dhcp_relay = rp->nbrp->dhcp_relay; > >>>>> + > >>>>> + 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_relay->servers, > &server_ip_str, > >>>>> + &server_ip, &port, > &addr_family)) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + if (server_ip_str == NULL) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + ds_put_format( > >>>>> + &match, "inport == %s && eth.src == %s && " > >>>>> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " > >>>>> + "udp.src == 68 && udp.dst == 67", > >>>>> + op->json_key, op->lsp_addrs[0].ea_s); > >>>>> + ds_put_format(&action, > >>>>> + "eth.dst=%s;outport=%s;next;/* DHCP_RELAY_REQ */", > >>>>> + rp->lrp_networks.ea_s,sp->json_key); > >>>>> + ovn_lflow_add_with_hint__(lflows, op->od, > >>>>> + S_SWITCH_IN_L2_LKUP, 100, > >>>>> + ds_cstr(&match), > >>>>> + ds_cstr(&action), > >>>>> + op->key, > >>>>> + NULL, > >>>>> + &lrp->header_); > >>>>> + free(server_ip_str); > >>>>> +} > >>>>> + > >>>>> static void > >>>>> build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, > >>>>> const struct ovn_port > *port, > >>>>> @@ -10181,6 +10257,13 @@ > build_lswitch_dhcp_options_and_response(struct ovn_port *op, > >>>>> return; > >>>>> } > >>>>> > >>>>> + if (op->od && op->od->nbs > >>>>> + && op->od->nbs->dhcp_relay_port) { > >>>>> + /* Don't add the DHCP server flows if DHCP Relay is enabled > on the > >>>>> + * logical switch. */ > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> bool is_external = lsp_is_external(op->nbsp); > >>>>> if (is_external && (!op->od->n_localnet_ports || > >>>>> !op->nbsp->ha_chassis_group)) { > >>>>> @@ -14458,6 +14541,86 @@ build_dhcpv6_reply_flows_for_lrouter_port( > >>>>> } > >>>>> } > >>>>> > >>>>> +static void > >>>>> +build_dhcp_relay_flows_for_lrouter_port( > >>>>> + struct ovn_port *op, struct hmap *lflows, > >>>>> + struct ds *match) > >>>>> +{ > >>>>> + if (!op->nbrp || !op->nbrp->dhcp_relay) { > >>>>> + return; > >>>>> + } > >>>>> + struct nbrec_dhcp_relay *dhcp_relay = op->nbrp->dhcp_relay; > >>>>> + if (!dhcp_relay->servers) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + int addr_family; > >>>>> + /* currently not supporting custom port */ > >>>>> + uint16_t port; > >>>>> + char *server_ip_str = NULL; > >>>>> + struct in6_addr server_ip; > >>>>> + > >>>>> + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, > &server_ip_str, > >>>>> + &server_ip, &port, > &addr_family)) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + if (server_ip_str == NULL) { > >>>>> + return; > >>>>> + } > >>>>> + > >>>>> + struct ds dhcp_action = DS_EMPTY_INITIALIZER; > >>>>> + ds_clear(match); > >>>>> + ds_put_format( > >>>>> + match, "inport == %s && " > >>>>> + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " > >>>>> + "udp.src == 68 && udp.dst == 67", > >>>>> + op->json_key); > >>>>> + ds_put_format(&dhcp_action, > >>>>> + "dhcp_relay_req(%s,%s);" > >>>>> + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* > DHCP_RELAY_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_RELAY_RESP */"); > >>>>> + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, > 110, > >>>>> + ds_cstr(match), ds_cstr(&dhcp_action), > >>>>> + &op->nbrp->header_); > >>>>> + > >>>>> + 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_relay_resp_fwd(%s,%s);ip4.src=%s;udp.dst=68;" > >>>>> + "outport=%s;output; /* DHCP_RELAY_RESP */", > >>>>> + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str, > >>>>> + op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key); > >>>>> + ovn_lflow_add_with_hint(lflows, op->od, > S_ROUTER_IN_DHCP_RELAY_RESP_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, > >>>>> @@ -15673,6 +15836,8 @@ 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_RELAY_RESP_FWD, 0, > "1", > >>>>> + "next;"); > >>>>> > >>>>> const char *ct_flag_reg = features->ct_no_masked_label > >>>>> ? "ct_mark" > >>>>> @@ -16154,6 +16319,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_relay_flows(op, lr_ports, lflows, > meter_groups); > >>>>> > >>>>> /* Build Logical Router Flows. */ > >>>>> build_ip_routing_flows_for_router_type_lsp(op, lr_ports, lflows); > >>>>> @@ -16183,6 +16349,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_relay_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 b2e0993e0..6863d52cd 100644 > >>>>> --- a/ovn-nb.ovsschema > >>>>> +++ b/ovn-nb.ovsschema > >>>>> @@ -1,7 +1,7 @@ > >>>>> { > >>>>> "name": "OVN_Northbound", > >>>>> - "version": "7.2.0", > >>>>> - "cksum": "1069338687 34162", > >>>>> + "version": "7.3.0", > >>>>> + "cksum": "2325497400 35185", > >>>>> "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_relay_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_relay": {"type": {"key": {"type": "uuid", > >>>>> + "refTable": > "DHCP_Relay", > >>>>> + "refType": "weak"}, > >>>>> + "min": 0, > >>>>> + "max": 1}}, > >>>>> "external_ids": { > >>>>> "type": {"key": "string", "value": "string", > >>>>> "min": 0, "max": "unlimited"}}, > >>>>> @@ -529,6 +539,15 @@ > >>>>> "type": {"key": "string", "value": "string", > >>>>> "min": 0, "max": "unlimited"}}}, > >>>>> "isRoot": true}, > >>>>> + "DHCP_Relay": { > >>>>> + "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 fcb1c6ecc..dc20892e1 100644 > >>>>> --- a/ovn-nb.xml > >>>>> +++ b/ovn-nb.xml > >>>>> @@ -608,6 +608,11 @@ > >>>>> Please see the <ref table="DNS"/> table. > >>>>> </column> > >>>>> > >>>>> + <column name="dhcp_relay_port"> > >>>>> + This column defines the <ref table="Logical_Router_Port"/> on > which > >>>>> + DHCP relay is enabled. > >>>>> + </column> > >>>>> + > >>>>> <column name="forwarding_groups"> > >>>>> Groups a set of logical port endpoints for traffic going out of > the > >>>>> logical switch. > >>>>> @@ -2980,6 +2985,11 @@ or > >>>>> port has all ingress and egress traffic dropped. > >>>>> </column> > >>>>> > >>>>> + <column name="dhcp_relay"> > >>>>> + This column is used to enabled DHCP Relay. Please refer > >>>>> + to <ref table="DHCP_Relay"/> table. > >>>>> + </column> > >>>>> + > >>>>> <group title="Distributed Gateway Ports"> > >>>>> <p> > >>>>> Gateways, as documented under <code>Gateways</code> in the OVN > >>>>> @@ -4286,6 +4296,24 @@ or > >>>>> </group> > >>>>> </table> > >>>>> > >>>>> + <table name="DHCP_Relay" title="DHCP Relay"> > >>>>> + <p> > >>>>> + OVN implements native DHCPv4 relay support which caters to > the common > >>>>> + use case of relaying the DHCP requests to external DHCP > server. > >>>>> + </p> > >>>>> + > >>>>> + <column name="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/atlocal.in b/tests/atlocal.in > >>>>> index 63d891b89..32d1c374e 100644 > >>>>> --- a/tests/atlocal.in > >>>>> +++ b/tests/atlocal.in > >>>>> @@ -187,6 +187,9 @@ fi > >>>>> # Set HAVE_DHCPD > >>>>> find_command dhcpd > >>>>> > >>>>> +# Set HAVE_DHCLIENT > >>>>> +find_command dhclient > >>>>> + > >>>>> # Set HAVE_BFDD_BEACON > >>>>> find_command bfdd-beacon > >>>>> > >>>>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > >>>>> index 19e4f1263..4d8c9ff26 100644 > >>>>> --- a/tests/ovn-northd.at > >>>>> +++ b/tests/ovn-northd.at > >>>>> @@ -8786,9 +8786,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 > >>>>> @@ -10966,3 +10966,38 @@ Status: active > >>>>> > >>>>> AT_CLEANUP > >>>>> ]) > >>>>> + > >>>>> +OVN_FOR_EACH_NORTHD_NO_HV([ > >>>>> +AT_SETUP([check DHCP RELAY AGENT]) > >>>>> +ovn_start NORTHD_TYPE > >>>>> + > >>>>> +check ovn-nbctl ls-add ls0 > >>>>> +check ovn-nbctl lsp-add ls0 ls0-port1 > >>>>> +check ovn-nbctl lsp-set-addresses ls0-port1 02:00:00:00:00:10 > >>>>> +check ovn-nbctl lr-add lr0 > >>>>> +check ovn-nbctl lrp-add lr0 lrp1 02:00:00:00:00:01 192.168.1.1/24 > >>>>> +check ovn-nbctl lsp-add ls0 lrp1-attachment > >>>>> +check ovn-nbctl lsp-set-type lrp1-attachment router > >>>>> +check ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02 > >>>>> +check ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1 > >>>>> +check ovn-nbctl lrp-add lr0 lrp-ext 02:00:00:00:00:02 > 192.168.2.1/24 > >>>>> + > >>>>> +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) > >>>>> +check ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay > >>>>> +rp_uuid=$(ovn-nbctl --bare --colum=_uuid list logical_router_port > lrp1) > >>>>> +check ovn-nbctl set Logical_Switch ls0 dhcp_relay_port=$rp_uuid > >>>>> + > >>>>> +check ovn-nbctl --wait=sb sync > >>>>> + > >>>>> +ovn-sbctl lflow-list > lflows > >>>>> +AT_CAPTURE_FILE([lflows]) > >>>>> + > >>>>> +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed > 's/table=../table=??/'], [0], [dnl > >>>>> + table=??(lr_in_ip_input ), priority=110 , match=(inport == > "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 > && udp.dst == 67), > action=(dhcp_relay_req(192.168.1.1,172.16.1.1);ip4.src=192.168.1.1;ip4.dst=172.16.1.1;udp.src=67;next; > /* DHCP_RELAY_REQ */) > >>>>> + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == > 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), > action=(next;/* DHCP_RELAY_RESP */) > >>>>> + table=??(lr_in_dhcp_relay_resp_fwd), priority=110 , > match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && > udp.dst == 67), action=(dhcp_rel _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev