Message ID | 20240424095607.129155-4-naveen.yerramneni@nutanix.com |
---|---|
State | Accepted |
Headers | show |
Series | DHCP Relay Agent support for overlay subnets. | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | warning | apply and check: warning |
ovsrobot/github-robot-_Build_and_Test | fail | github build: failed |
ovsrobot/github-robot-_ovn-kubernetes | fail | github build: failed |
References: <20240424095607.129155-4-naveen.yerramneni@nutanix.com> Bleep bloop. Greetings Naveen Yerramneni, I am a robot and I have tried out your patch. Thanks for your contribution. I encountered some error that I wasn't expecting. See the details below. checkpatch: WARNING: Line is 101 characters long (recommended limit is 79) #136 FILE: northd/northd.c:237: * | R2 REG_DHCP_RELAY_DIP_IPV4 | X | | 0 | | Lines checked: 1474, Warnings: 1, Errors: 0 Please check this out. If you feel there has been an error, please email aconole@redhat.com Thanks, 0-day Robot
On Wed, Apr 24, 2024 at 5:57 AM Naveen Yerramneni < naveen.yerramneni@nutanix.com> wrote: > NB SCHEMA CHANGES > ----------------- > 1. New DHCP_Relay table > "DHCP_Relay": { > "columns": { > "name": {"type": "string"}, > "servers": {"type": {"key": "string", > "min": 0, > "max": 1}}, > "external_ids": { > "type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}}, > "options": {"type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}, > "isRoot": true}, > 2. New column to Logical_Router_Port table > "dhcp_relay": {"type": {"key": {"type": "uuid", > "refTable": "DHCP_Relay", > "refType": "strong"}, > "min": 0, > "max": 1}}, > > NEW PIPELINE STAGES > ------------------- > Following stage is added for DHCP relay feature. > Some of the flows are fitted into the existing pipeline tages. > 1. lr_in_dhcp_relay_req > - This stage process the DHCP request packets coming from DHCP > clients. > - DHCP request packets for which dhcp_relay_req_chk action > (which gets applied in ip input stage) is successful are > forwarded to DHCP server. > - DHCP request packets for which dhcp_relay_req_chk action is > unsuccessful gets dropped. > 2. lr_in_dhcp_relay_resp_chk > - This stage applied the dhcp_relay_resp_chk action for DHCP > response packets coming > from the DHCP server. > 3. lr_in_dhcp_relay_resp > - DHCP response packets for which dhcp_relay_resp_chk is sucessful > are forwarded > to the DHCP clients. > - DHCP response packets for which dhcp_relay_resp_chk is > unsucessful gets dropped. > > REGISTRY USAGE > --------------- > - reg9[7] : To store the result of dhcp_relay_req_chk action. > - reg9[8] : To store the result of dhcp_relay_resp_chk action. > - reg2 : To store the original dest ip for DHCP response packets. > This is required to properly match the packets in > lr_in_dhcp_relay_resp stage since dhcp_relay_resp_chk action > changes the dest ip. > > FLOWS > ----- > > Following are the flows added when DHCP Relay is configured on one overlay > subnet, > one additonal flow is added in ls_in_l2_lkup table for each VM part of the > subnet. > > 1. table=27(ls_in_l2_lkup ), priority=100 , match=(inport == > <vm_port> && eth.src == <vm_mac> && ip4.src == 0.0.0.0 && ip4.dst == > 255.255.255.255 && udp.src == 68 && udp.dst == 67), > action=(eth.dst=<lrp_mac>;outport=<lrp>;next;/* DHCP_RELAY_REQ */) > 2. table=3 (lr_in_ip_input ), priority=110 , match=(inport == <lrp> > && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && ip.frag == 0 && > udp.src == 68 && udp.dst == 67), > action=(reg9[7] = dhcp_relay_req_chk(<lrp_ip>, > <dhcp_server_ip>);next; /* DHCP_RELAY_REQ */) > 3. table=3 (lr_in_ip_input ), priority=110 , match=(ip4.src == > <dhcp_server> && ip4.dst == <lrp> && udp.src == 67 && udp.dst == 67), > action=(next;/* DHCP_RELAY_RESP */) > 4. table=4 (lr_in_dhcp_relay_req), priority=100 , match=(inport == > "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 > && udp.dst == 67 && reg9[7]), > action=(ip4.src=<lrp>;ip4.dst=<dhcp_server>;udp.src=67;next; /* > DHCP_RELAY_REQ */) > 5. table=4 (lr_in_dhcp_relay_req), priority=1 , match=(inport == > <lrp> && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 > && udp.dst == 67 && reg9[7] == 0), > action=(drop; /* DHCP_RELAY_REQ */) > 6. table=18(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src > == <dhcp_server> && ip4.dst == <lrp> && ip.frag == 0 && udp.src == 67 && > udp.dst == 67), > action=(reg2 = ip4.dst;reg9[8] = dhcp_relay_resp_chk(<lrp_ip>, > <dhcp_server_ip>);next;/* DHCP_RELAY_RESP */) > 7. table=19(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == > <dhcp_server> && reg2 == <lrp_ip> && udp.src == 67 && udp.dst == 67 && > reg9[8]), > action=(ip4.src=<lrp>;udp.dst=68;outport=<lrp>;output; /* > DHCP_RELAY_RESP */) > 8. table=19(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src == > <dhcp_server> && reg2 == <lrp_ip> && udp.src == 67 && udp.dst == 67 && > reg9[8] == 0), action=(drop; /* DHCP_RELAY_RESP */) > > Commands to enable the feature > ------------------------------ > ovn-nbctl create DHCP_Relay name=<name> servers=<dhcp_server_ip> > ovn-nbctl set Logical_Router_port <lrp> dhcp_relay=<relay_uuid> > ovn-nbctl set Logical_Switch <ls> > other_config:dhcp_relay_port=<router_patch_port> > > 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. > > Signed-off-by: Naveen Yerramneni <naveen.yerramneni@nutanix.com> > Co-authored-by: Huzaifa Calcuttawala <huzaifa.c@nutanix.com> > Signed-off-by: Huzaifa Calcuttawala <huzaifa.c@nutanix.com> > CC: Mary Manohar <mary.manohar@nutanix.com> > Thanks. I applied this patch to the main with the below changes. I also added a NEWS entry for this feature and enhanced the ovn-northd.at test case a bit. ---------------------------------------------------------------------------- diff --git a/northd/northd.c b/northd/northd.c index 9fa3ec96cb..b541e8455c 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -91,6 +91,7 @@ static bool use_ct_inv_match = true; static bool default_acl_drop; #define MAX_OVN_TAGS 4096 + /* Due to various hard-coded priorities need to implement ACLs, the * northbound database supports a smaller range of ACL priorities than @@ -8574,8 +8575,7 @@ build_lswitch_dhcp_relay_flows(struct ovn_port *op, return; } - if (!op->od || !op->od->n_router_ports || - !op->od->nbs) { + if (!op->od || !op->od->n_router_ports || !op->od->nbs) { return; } @@ -8621,8 +8621,8 @@ build_lswitch_dhcp_relay_flows(struct ovn_port *op, "udp.src == 68 && udp.dst == 67", op->json_key, op->lsp_addrs[0].ea_s); ds_put_format(actions, - "eth.dst=%s;outport=%s;next;/* DHCP_RELAY_REQ */", - rp->lrp_networks.ea_s,sp->json_key); + "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), @@ -13724,10 +13724,10 @@ build_dhcpv6_reply_flows_for_lrouter_port( } static void -build_dhcp_relay_flows_for_lrouter_port( - struct ovn_port *op, struct lflow_table *lflows, - struct ds *match, struct ds *actions, - struct lflow_ref *lflow_ref) +build_dhcp_relay_flows_for_lrouter_port(struct ovn_port *op, + struct lflow_table *lflows, + struct ds *match, struct ds *actions, + struct lflow_ref *lflow_ref) { if (!op->nbrp || !op->nbrp->dhcp_relay) { return; @@ -13772,10 +13772,10 @@ build_dhcp_relay_flows_for_lrouter_port( "ip.frag == 0 && udp.src == 68 && udp.dst == 67", op->json_key); ds_put_format(actions, - REGBIT_DHCP_RELAY_REQ_CHK - " = dhcp_relay_req_chk(%s, %s);" - "next; /* DHCP_RELAY_REQ */", - op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + REGBIT_DHCP_RELAY_REQ_CHK + " = dhcp_relay_req_chk(%s, %s);" + "next; /* DHCP_RELAY_REQ */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, ds_cstr(match), ds_cstr(actions), @@ -13791,8 +13791,9 @@ build_dhcp_relay_flows_for_lrouter_port( REGBIT_DHCP_RELAY_REQ_CHK, op->json_key); ds_put_format(actions, - "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_RELAY_REQ */", - op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + "ip4.src = %s; ip4.dst = %s; udp.src = 67; next; " + "/* DHCP_RELAY_REQ */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 100, ds_cstr(match), ds_cstr(actions), @@ -13807,8 +13808,7 @@ build_dhcp_relay_flows_for_lrouter_port( "udp.src == 68 && udp.dst == 67 && " REGBIT_DHCP_RELAY_REQ_CHK" == 0", op->json_key); - ds_put_format(actions, - "drop; /* DHCP_RELAY_REQ */"); + ds_put_format(actions, "drop; /* DHCP_RELAY_REQ */"); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 1, ds_cstr(match), ds_cstr(actions), @@ -13821,7 +13821,7 @@ build_dhcp_relay_flows_for_lrouter_port( match, "ip4.src == %s && ip4.dst == %s && " "ip.frag == 0 && udp.src == 67 && udp.dst == 67", server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); - ds_put_format(actions, "next;/* DHCP_RELAY_RESP */"); + ds_put_format(actions, "next; /* DHCP_RELAY_RESP */"); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, ds_cstr(match), ds_cstr(actions), &op->nbrp->header_, lflow_ref); @@ -13834,10 +13834,10 @@ build_dhcp_relay_flows_for_lrouter_port( "udp.src == 67 && udp.dst == 67", server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); ds_put_format(actions, - REG_DHCP_RELAY_DIP_IPV4" = ip4.dst;" - REGBIT_DHCP_RELAY_RESP_CHK - " = dhcp_relay_resp_chk(%s, %s);next;/* DHCP_RELAY_RESP */", - op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + REG_DHCP_RELAY_DIP_IPV4" = ip4.dst; " + REGBIT_DHCP_RELAY_RESP_CHK + " = dhcp_relay_resp_chk(%s, %s); next; /* DHCP_RELAY_RESP */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, 100, @@ -13855,28 +13855,24 @@ build_dhcp_relay_flows_for_lrouter_port( REGBIT_DHCP_RELAY_RESP_CHK, server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); ds_put_format(actions, - "ip4.src=%s;udp.dst=68;" - "outport=%s;output; /* DHCP_RELAY_RESP */", - op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key); + "ip4.src = %s; udp.dst = 68; " + "outport = %s; output; /* DHCP_RELAY_RESP */", + op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, - 100, - ds_cstr(match), ds_cstr(actions), + 100, ds_cstr(match), ds_cstr(actions), &op->nbrp->header_, lflow_ref); ds_clear(match); ds_clear(actions); - ds_put_format( - match, "ip4.src == %s && " - REG_DHCP_RELAY_DIP_IPV4" == %s && " - "udp.src == 67 && udp.dst == 67 && " - REGBIT_DHCP_RELAY_RESP_CHK" == 0", - server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); - ds_put_format(actions, - "drop; /* DHCP_RELAY_RESP */"); + ds_put_format(match, "ip4.src == %s && " + REG_DHCP_RELAY_DIP_IPV4" == %s && " + "udp.src == 67 && udp.dst == 67 && " + REGBIT_DHCP_RELAY_RESP_CHK" == 0", + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); + ds_put_format(actions, "drop; /* DHCP_RELAY_RESP */"); ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, - 1, - ds_cstr(match), ds_cstr(actions), + 1, ds_cstr(match), ds_cstr(actions), &op->nbrp->header_, lflow_ref); ds_clear(match); ds_clear(actions); diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 297b6aceaf..27183c551c 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -12288,13 +12288,47 @@ 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 && ip.frag == 0 && udp.src == 68 && udp.dst == 67), action=(reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);next; /* DHCP_RELAY_REQ */) - table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && ip.frag == 0 && udp.src == 67 && udp.dst == 67), action=(next;/* DHCP_RELAY_RESP */) - table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]]), action=(ip4.src=192.168.1.1;ip4.dst=172.16.1.1;udp.src=67;next; /* DHCP_RELAY_REQ */) + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && ip.frag == 0 && udp.src == 67 && udp.dst == 67), action=(next; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]]), action=(ip4.src = 192.168.1.1; ip4.dst = 172.16.1.1; udp.src = 67; next; /* DHCP_RELAY_REQ */) table=??(lr_in_dhcp_relay_req), priority=1 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]] == 0), action=(drop; /* DHCP_RELAY_REQ */) - table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), action=(reg2 = ip4.dst;reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);next;/* DHCP_RELAY_RESP */) - table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]]), action=(ip4.src=192.168.1.1;udp.dst=68;outport="lrp1";output; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), action=(reg2 = ip4.dst; reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1); next; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]]), action=(ip4.src = 192.168.1.1; udp.dst = 68; outport = "lrp1"; output; /* DHCP_RELAY_RESP */) table=??(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]] == 0), action=(drop; /* DHCP_RELAY_RESP */) - table=??(ls_in_l2_lkup ), priority=100 , match=(inport == "ls0-port1" && eth.src == 02:00:00:00:00:10 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(eth.dst=02:00:00:00:00:01;outport="lrp1-attachment";next;/* DHCP_RELAY_REQ */) + table=??(ls_in_l2_lkup ), priority=100 , match=(inport == "ls0-port1" && eth.src == 02:00:00:00:00:10 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(eth.dst = 02:00:00:00:00:01; outport = "lrp1-attachment"; next; /* DHCP_RELAY_REQ */) +]) + +# Set invalid dhcp_relay_port in ls0 +check ovn-nbctl --wait=sb set Logical_Switch ls0 other_config:dhcp_relay_port=foo + +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 && ip.frag == 0 && udp.src == 68 && udp.dst == 67), action=(reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);next; /* DHCP_RELAY_REQ */) + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && ip.frag == 0 && udp.src == 67 && udp.dst == 67), action=(next; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]]), action=(ip4.src = 192.168.1.1; ip4.dst = 172.16.1.1; udp.src = 67; next; /* DHCP_RELAY_REQ */) + table=??(lr_in_dhcp_relay_req), priority=1 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]] == 0), action=(drop; /* DHCP_RELAY_REQ */) + table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), action=(reg2 = ip4.dst; reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1); next; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]]), action=(ip4.src = 192.168.1.1; udp.dst = 68; outport = "lrp1"; output; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]] == 0), action=(drop; /* DHCP_RELAY_RESP */) +]) + +# Clear the dhcp_relay from lrp1 +check ovn-nbctl --wait=sb clear Logical_Router_port lrp1 dhcp_relay + +ovn-sbctl lflow-list > lflows +AT_CAPTURE_FILE([lflows]) + +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed 's/table=../table=??/'], [0], [dnl +]) + +# set the dhcp_relay_port in ls0 to proper one. +check ovn-nbctl --wait=sb set Logical_Switch ls0 other_config:dhcp_relay_port=lrp1-attachment + +ovn-sbctl lflow-list > lflows +AT_CAPTURE_FILE([lflows]) + +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed 's/table=../table=??/'], [0], [dnl ]) AT_CLEANUP ---------------------------------------------------------------------------------------- > --- > northd/northd.c | 271 +++++++++++++++++++++++++++++++++++++++- > northd/northd.h | 41 +++--- > northd/ovn-northd.8.xml | 211 +++++++++++++++++++++++++++---- > ovn-nb.ovsschema | 21 +++- > ovn-nb.xml | 39 ++++++ > tests/atlocal.in | 3 + > tests/ovn-northd.at | 38 ++++++ > tests/ovn.at | 224 ++++++++++++++++++++++++++++++++- > tests/system-ovn.at | 148 ++++++++++++++++++++++ > 9 files changed, 948 insertions(+), 48 deletions(-) > > diff --git a/northd/northd.c b/northd/northd.c > index 331d9c267..045c3576b 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -91,7 +91,6 @@ static bool use_ct_inv_match = true; > static bool default_acl_drop; > > #define MAX_OVN_TAGS 4096 > - > > /* Due to various hard-coded priorities need to implement ACLs, the > * northbound database supports a smaller range of ACL priorities than > @@ -153,6 +152,8 @@ static bool default_acl_drop; > #define REGBIT_LOOKUP_NEIGHBOR_IP_RESULT "reg9[3]" > #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]" > #define REGBIT_KNOWN_LB_SESSION "reg9[6]" > +#define REGBIT_DHCP_RELAY_REQ_CHK "reg9[7]" > +#define REGBIT_DHCP_RELAY_RESP_CHK "reg9[8]" > > /* Register to store the eth address associated to a router port for > packets > * received in S_ROUTER_IN_ADMISSION. > @@ -168,6 +169,7 @@ static bool default_acl_drop; > #define REG_NEXT_HOP_IPV6 "xxreg0" > #define REG_SRC_IPV4 "reg1" > #define REG_SRC_IPV6 "xxreg1" > +#define REG_DHCP_RELAY_DIP_IPV4 "reg2" > #define REG_ROUTE_TABLE_ID "reg7" > > /* Register used to store backend ipv6 address > @@ -232,7 +234,7 @@ static bool default_acl_drop; > * | R1 | SRC_IPV4 for ARP-REQ | 0 | | R | > | > * | | (>= IP_INPUT) | | | E | > NEXT_HOP_IPV6 (>= DEFRAG ) | > * +-----+---------------------------+---+-----------------+ G | > | > - * | R2 | UNUSED | X | | 0 | > | > + * | R2 REG_DHCP_RELAY_DIP_IPV4 | X | | 0 | > | > * | | | R | | | > | > * +-----+---------------------------+ E | UNUSED | | > | > * | R3 | UNUSED | G | | | > | > @@ -259,7 +261,9 @@ static bool default_acl_drop; > * | | EGRESS_LOOPBACK/ | G | UNUSED | > * | R9 | PKT_LARGER/ | 4 | | > * | | LOOKUP_NEIGHBOR_RESULT/ | | | > - * | | SKIP_LOOKUP_NEIGHBOR} | | | > + * | | SKIP_LOOKUP_NEIGHBOR/ | | | > + * | |REGBIT_DHCP_RELAY_REQ_CHK/ | | | > + * | |REGBIT_DHCP_RELAY_RESP_CHK}| | | > * | | | | | > * | | REG_ORIG_TP_DPORT_ROUTER | | | > * | | | | | > @@ -8543,6 +8547,90 @@ build_dhcpv6_options_flows(struct ovn_port *op, > ds_destroy(&match); > } > > +static const char * > +ls_dhcp_relay_port(const struct ovn_datapath *od) > +{ > + return smap_get(&od->nbs->other_config, "dhcp_relay_port"); > +} > + > +static void > +build_lswitch_dhcp_relay_flows(struct ovn_port *op, > + const struct hmap *ls_ports, > + struct lflow_table *lflows, > + struct ds *match, > + struct ds *actions) > +{ > + if (op->nbrp || !op->nbsp) { > + return; > + } > + > + /* consider only ports attached to VMs */ > + if (strcmp(op->nbsp->type, "")) { > + return; > + } > + > + if (!op->od || !op->od->n_router_ports || > + !op->od->nbs) { > + return; > + } > + > + /* configure dhcp relay flows only when peer router has > + * relay config enabled */ > + const char *dhcp_relay_port = ls_dhcp_relay_port(op->od); > + if (!dhcp_relay_port) { > + return; > + } > + > + struct ovn_port *sp = ovn_port_find(ls_ports, dhcp_relay_port); > + > + if (!sp || !sp->nbsp || !sp->peer) { > + return; > + } > + > + struct ovn_port *rp = sp->peer; > + if (!rp || !rp->nbrp || !rp->nbrp->dhcp_relay || rp->peer != sp) { > + return; > + } > + > + char *server_ip_str = NULL; > + uint16_t port; > + int addr_family; > + struct in6_addr server_ip; > + struct nbrec_dhcp_relay *dhcp_relay = rp->nbrp->dhcp_relay; > + > + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, > &server_ip_str, > + &server_ip, &port, > &addr_family)) { > + return; > + } > + > + if (server_ip_str == NULL) { > + return; > + } > + > + ds_clear(match); > + ds_clear(actions); > + > + 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(actions, > + "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(actions), > + op->key, > + NULL, > + &op->nbsp->header_, > + op->lflow_ref); > + ds_clear(match); > + ds_clear(actions); > + free(server_ip_str); > +} > + > static void > build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, > const struct ovn_port > *port, > @@ -9144,6 +9232,13 @@ build_lswitch_dhcp_options_and_response(struct > ovn_port *op, > return; > } > > + if (op->od && op->od->nbs > + && ls_dhcp_relay_port(op->od)) { > + /* Don't add the DHCP server flows if DHCP Relay is enabled on the > + * logical switch. */ > + return; > + } > + > bool is_external = lsp_is_external(op->nbsp); > if (is_external && (!op->od->n_localnet_ports || > !op->nbsp->ha_chassis_group)) { > @@ -13623,6 +13718,166 @@ build_dhcpv6_reply_flows_for_lrouter_port( > } > } > > +static void > +build_dhcp_relay_flows_for_lrouter_port( > + struct ovn_port *op, struct lflow_table *lflows, > + struct ds *match, struct ds *actions, > + struct lflow_ref *lflow_ref) > +{ > + if (!op->nbrp || !op->nbrp->dhcp_relay) { > + return; > + > + } > + > + /* configure dhcp relay flows only when peer switch has > + * relay config enabled */ > + struct ovn_port *sp = op->peer; > + if (!sp || !sp->nbsp || sp->peer != op || > + !sp->od || !ls_dhcp_relay_port(sp->od)) { > + return; > + } > + > + struct nbrec_dhcp_relay *dhcp_relay = op->nbrp->dhcp_relay; > + if (!dhcp_relay->servers) { > + return; > + } > + > + int addr_family; > + /* currently not supporting custom port, > + * dhcp server port is always set to 67 when installing flows */ > + uint16_t port; > + char *server_ip_str = NULL; > + struct in6_addr server_ip; > + > + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, > &server_ip_str, > + &server_ip, &port, > &addr_family)) { > + return; > + } > + > + if (server_ip_str == NULL) { > + return; > + } > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "inport == %s && " > + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " > + "ip.frag == 0 && udp.src == 68 && udp.dst == 67", > + op->json_key); > + ds_put_format(actions, > + REGBIT_DHCP_RELAY_REQ_CHK > + " = dhcp_relay_req_chk(%s, %s);" > + "next; /* DHCP_RELAY_REQ */", > + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); > + > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "inport == %s && " > + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " > + "udp.src == 68 && udp.dst == 67 && " > + REGBIT_DHCP_RELAY_REQ_CHK, > + op->json_key); > + ds_put_format(actions, > + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_RELAY_REQ */", > + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); > + > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, > 100, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "inport == %s && " > + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " > + "udp.src == 68 && udp.dst == 67 && " > + REGBIT_DHCP_RELAY_REQ_CHK" == 0", > + op->json_key); > + ds_put_format(actions, > + "drop; /* DHCP_RELAY_REQ */"); > + > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 1, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "ip4.src == %s && ip4.dst == %s && " > + "ip.frag == 0 && udp.src == 67 && udp.dst == 67", > + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); > + ds_put_format(actions, "next;/* DHCP_RELAY_RESP */"); > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + ds_clear(match); > + ds_clear(actions); > + > + 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(actions, > + REG_DHCP_RELAY_DIP_IPV4" = ip4.dst;" > + REGBIT_DHCP_RELAY_RESP_CHK > + " = dhcp_relay_resp_chk(%s, %s);next;/* DHCP_RELAY_RESP */", > + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); > + > + ovn_lflow_add_with_hint(lflows, op->od, > S_ROUTER_IN_DHCP_RELAY_RESP_CHK, > + 100, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "ip4.src == %s && " > + REG_DHCP_RELAY_DIP_IPV4" == %s && " > + "udp.src == 67 && udp.dst == 67 && " > + REGBIT_DHCP_RELAY_RESP_CHK, > + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); > + ds_put_format(actions, > + "ip4.src=%s;udp.dst=68;" > + "outport=%s;output; /* DHCP_RELAY_RESP */", > + op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key); > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, > + 100, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + > + ds_clear(match); > + ds_clear(actions); > + > + ds_put_format( > + match, "ip4.src == %s && " > + REG_DHCP_RELAY_DIP_IPV4" == %s && " > + "udp.src == 67 && udp.dst == 67 && " > + REGBIT_DHCP_RELAY_RESP_CHK" == 0", > + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); > + ds_put_format(actions, > + "drop; /* DHCP_RELAY_RESP */"); > + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, > + 1, > + ds_cstr(match), ds_cstr(actions), > + &op->nbrp->header_, lflow_ref); > + ds_clear(match); > + ds_clear(actions); > + free(server_ip_str); > +} > + > static void > build_ipv6_input_flows_for_lrouter_port( > struct ovn_port *op, struct lflow_table *lflows, > @@ -14893,6 +15148,13 @@ static void > build_lr_nat_defrag_and_lb_default_flows( > lflow_ref); > ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;", > lflow_ref); > + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_REQ, 0, "1", > + "next;", lflow_ref); > + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, 0, "1", > + "next;", lflow_ref); > + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP, 0, "1", > + "next;", lflow_ref); > + > > /* Send the IPv6 NS packets to next table. When ovn-controller > * generates IPv6 NS (for the action - nd_ns{}), the injected > @@ -15652,6 +15914,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct > ovn_port *op, > build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, > actions); > build_lswitch_ip_unicast_lookup(op, lflows, actions, > match); > + build_lswitch_dhcp_relay_flows(op, ls_ports, lflows, match, actions); > > /* Build Logical Router Flows. */ > build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions); > @@ -15681,6 +15944,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct > ovn_port *op, > op->lflow_ref); > build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, > &lsi->match, > op->lflow_ref); > + build_dhcp_relay_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, > + &lsi->actions, op->lflow_ref); > build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows, > &lsi->match, &lsi->actions, > lsi->meter_groups, > diff --git a/northd/northd.h b/northd/northd.h > index 18cad5234..940926945 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -441,24 +441,29 @@ enum ovn_stage { > PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, > "lr_in_lookup_neighbor") \ > PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, > "lr_in_learn_neighbor") \ > PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") > \ > - PIPELINE_STAGE(ROUTER, IN, UNSNAT, 4, "lr_in_unsnat") > \ > - PIPELINE_STAGE(ROUTER, IN, DEFRAG, 5, "lr_in_defrag") > \ > - PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 6, "lr_in_lb_aff_check") > \ > - PIPELINE_STAGE(ROUTER, IN, DNAT, 7, "lr_in_dnat") > \ > - PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 8, "lr_in_lb_aff_learn") > \ > - PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 9, > "lr_in_ecmp_stateful") \ > - PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 10, > "lr_in_nd_ra_options") \ > - PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 11, > "lr_in_nd_ra_response") \ > - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 12, > "lr_in_ip_routing_pre") \ > - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 13, "lr_in_ip_routing") > \ > - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 14, > "lr_in_ip_routing_ecmp") \ > - PIPELINE_STAGE(ROUTER, IN, POLICY, 15, "lr_in_policy") > \ > - PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 16, > "lr_in_policy_ecmp") \ > - PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 17, > "lr_in_arp_resolve") \ > - PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 18, > "lr_in_chk_pkt_len") \ > - PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 19, > "lr_in_larger_pkts") \ > - PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 20, > "lr_in_gw_redirect") \ > - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 21, > "lr_in_arp_request") \ > + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_REQ, 4, > "lr_in_dhcp_relay_req") \ > + PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") > \ > + PIPELINE_STAGE(ROUTER, IN, DEFRAG, 6, "lr_in_defrag") > \ > + PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 7, "lr_in_lb_aff_check") > \ > + PIPELINE_STAGE(ROUTER, IN, DNAT, 8, "lr_in_dnat") > \ > + PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 9, "lr_in_lb_aff_learn") > \ > + PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 10, > "lr_in_ecmp_stateful") \ > + PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 11, > "lr_in_nd_ra_options") \ > + PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 12, > "lr_in_nd_ra_response") \ > + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 13, > "lr_in_ip_routing_pre") \ > + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 14, "lr_in_ip_routing") > \ > + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 15, > "lr_in_ip_routing_ecmp") \ > + PIPELINE_STAGE(ROUTER, IN, POLICY, 16, "lr_in_policy") > \ > + PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 17, > "lr_in_policy_ecmp") \ > + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 18, > \ > + "lr_in_dhcp_relay_resp_chk") > \ > + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 19, > \ > + "lr_in_dhcp_relay_resp") > \ > + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 20, > "lr_in_arp_resolve") \ > + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 21, > "lr_in_chk_pkt_len") \ > + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 22, > "lr_in_larger_pkts") \ > + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 23, > "lr_in_gw_redirect") \ > + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 24, > "lr_in_arp_request") \ > \ > /* Logical router egress stages. */ \ > PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0, > \ > diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml > index b14a30285..1e7fb3888 100644 > --- a/northd/ovn-northd.8.xml > +++ b/northd/ovn-northd.8.xml > @@ -1934,6 +1934,12 @@ output; > logical switch. > </li> > > + <li> > + A priority-100 flow that forwards all DHCP broadcast packets > coming > + from VIFs to the logical router port's MAC when DHCP relay is > enabled > + on the logical switch. > + </li> > + > <li> > Priority-90 flows for transit switches that forward registered > IP multicast traffic to their corresponding multicast group , > which > @@ -2864,6 +2870,44 @@ icmp6_error { > </p> > </li> > > + <li> > + <p> > + For each logical router port configured with DHCP relay the > + following priority-110 flows are added to manage the DHCP > relay > + traffic: > + > + <ul> > + <li> > + <p> > + if <code>inport</code> is lrp and <code>ip4.src == > 0.0.0.0 > + </code> and <code>ip4.dst == 255.255.255.255</code> and > + <code>ip4.frag == 0 </code> and <code>udp.src == > 68</code> > + and <code>udp.dst == 67</code>, the > <code>dhcp_relay_req_chk > + </code> action is executed. > + </p> > + > + <pre> > + reg9[7] = dhcp_relay_req_chk(<var>lrp_ip</var>, > + > <var>dhcp_server_ip</var>);next > + </pre> > + > + <p> > + if action is successful then, GIADDR in the dhcp header is > + updated with lrp ip and stores 1 into reg9[7] else stores > 0 > + into reg9[7]. > + </p> > + </li> > + > + <li> > + if <code>ip4.src</code> is DHCP server ip and <code>ip4.dst > + </code> is lrp IP and <code>udp.src == 67</code> and > + <code>udp.dst == 67</code>, the packet is advanced to the > next > + pipeline stage. > + </li> > + </ul> > + </p> > + </li> > + > <li> > <p> > L3 admission control: Priority-120 flows allows IGMP and MLD > packets > @@ -3328,8 +3372,50 @@ icmp6 { > </ul> > > > + <h3>Ingress Table 4: DHCP Relay Request</h3> > + <p> > + This stage process the DHCP request packets on which > + <code>dhcp_relay_req_chk</code> action is applied in the IP input > stage. > + </p> > + <ul> > + <li> > + <p> > + A priority-100 logical flow is added for each logical router > port > + configured with DHCP relay that matches <code>inport</code> is > lrp > + and <code>ip4.src == 0.0.0.0</code> and > + <code>ip4.dst == 255.255.255.255</code> and <code>udp.src == 68 > + </code> and <code>udp.dst == 67</code> and <code>reg9[7] == > 1</code> > + and applies following actions. If <code>reg9[7]</code> is set > to 1 > + then, <code>dhcp_relay_req_chk</code> action was successful. > + </p> > + > + <pre> > +ip4.src=<var>lrp ip</var>; > +ip4.dst=<var>dhcp server ip</var>; > +udp.src = 67; > +next; > + </pre> > + </li> > + > + <li> > + <p> > + A priority-1 logical flow is added for each logical router port > + configured with DHCP relay that matches <code>inport</code> is > lrp > + and <code>ip4.src == 0.0.0.0</code> and > + <code>ip4.dst == 255.255.255.255</code> and <code>udp.src == 68 > + </code> and <code>udp.dst == 67</code> and <code>reg9[7] == > 0</code> > + and drops the packet. If <code>reg9[7]</code> is set to 0 then, > + <code>dhcp_relay_req_chk</code> action was unsuccessful. > + </p> > + </li> > + > + <li> > + A priority-0 flow that matches all packets to advance to the next > + table. > + </li> > + </ul> > > - <h3>Ingress Table 4: UNSNAT</h3> > + <h3>Ingress Table 5: UNSNAT</h3> > > <p> > This is for already established connections' reverse traffic. > @@ -3338,7 +3424,7 @@ icmp6 { > unSNATted here. > </p> > > - <p>Ingress Table 4: UNSNAT on Gateway and Distributed Routers</p> > + <p>Ingress Table 5: UNSNAT on Gateway and Distributed Routers</p> > <ul> > <li> > <p> > @@ -3365,7 +3451,7 @@ icmp6 { > </li> > </ul> > > - <p>Ingress Table 4: UNSNAT on Gateway Routers</p> > + <p>Ingress Table 5: UNSNAT on Gateway Routers</p> > > <ul> > <li> > @@ -3414,7 +3500,7 @@ icmp6 { > </li> > </ul> > > - <p>Ingress Table 4: UNSNAT on Distributed Routers</p> > + <p>Ingress Table 5: UNSNAT on Distributed Routers</p> > > <ul> > <li> > @@ -3461,7 +3547,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 5: DEFRAG</h3> > + <h3>Ingress Table 6: DEFRAG</h3> > > <p> > This is to send packets to connection tracker for tracking and > @@ -3504,7 +3590,7 @@ icmp6 { > this allows potentially related ICMP traffic to pass through CT. > </p> > > - <h3>Ingress Table 6: Load balancing affinity check</h3> > + <h3>Ingress Table 7: Load balancing affinity check</h3> > > <p> > Load balancing affinity check table contains the following > @@ -3531,7 +3617,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 7: DNAT</h3> > + <h3>Ingress Table 8: DNAT</h3> > > <p> > Packets enter the pipeline with destination IP address that needs to > @@ -3539,7 +3625,7 @@ icmp6 { > in the reverse direction needs to be unDNATed. > </p> > > - <p>Ingress Table 7: Load balancing DNAT rules</p> > + <p>Ingress Table 8: Load balancing DNAT rules</p> > > <p> > Following load balancing DNAT flows are added for Gateway router or > @@ -3660,7 +3746,7 @@ icmp6 { > </li> > </ul> > > - <p>Ingress Table 7: DNAT on Gateway Routers</p> > + <p>Ingress Table 8: DNAT on Gateway Routers</p> > > <ul> > <li> > @@ -3702,7 +3788,7 @@ icmp6 { > </li> > </ul> > > - <p>Ingress Table 7: DNAT on Distributed Routers</p> > + <p>Ingress Table 8: DNAT on Distributed Routers</p> > > <p> > On distributed routers, the DNAT table only handles packets > @@ -3757,7 +3843,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 8: Load balancing affinity learn</h3> > + <h3>Ingress Table 9: Load balancing affinity learn</h3> > > <p> > Load balancing affinity learn table contains the following > @@ -3785,7 +3871,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 9: ECMP symmetric reply processing</h3> > + <h3>Ingress Table 10: ECMP symmetric reply processing</h3> > <ul> > <li> > If ECMP routes with symmetric reply are configured in the > @@ -3804,7 +3890,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 10: IPv6 ND RA option processing</h3> > + <h3>Ingress Table 11: IPv6 ND RA option processing</h3> > > <ul> > <li> > @@ -3834,7 +3920,7 @@ reg0[5] = put_nd_ra_opts(<var>options</var>);next; > </li> > </ul> > > - <h3>Ingress Table 11: IPv6 ND RA responder</h3> > + <h3>Ingress Table 12: IPv6 ND RA responder</h3> > > <p> > This table implements IPv6 ND RA responder for the IPv6 ND RA > replies > @@ -3879,7 +3965,7 @@ output; > </li> > </ul> > > - <h3>Ingress Table 12: IP Routing Pre</h3> > + <h3>Ingress Table 13: IP Routing Pre</h3> > > <p> > If a packet arrived at this table from Logical Router Port > <var>P</var> > @@ -3909,7 +3995,7 @@ output; > </li> > </ul> > > - <h3>Ingress Table 13: IP Routing</h3> > + <h3>Ingress Table 14: IP Routing</h3> > > <p> > A packet that arrives at this table is an IP packet that should be > @@ -4115,7 +4201,7 @@ select(reg8[16..31], <var>MID1</var>, > <var>MID2</var>, ...); > </li> > </ul> > > - <h3>Ingress Table 14: IP_ROUTING_ECMP</h3> > + <h3>Ingress Table 15: IP_ROUTING_ECMP</h3> > > <p> > This table implements the second part of IP routing for ECMP routes > @@ -4172,7 +4258,7 @@ outport = <var>P</var>; > </li> > </ul> > > - <h3>Ingress Table 15: Router policies</h3> > + <h3>Ingress Table 16: Router policies</h3> > <p> > This table adds flows for the logical router policies configured > on the logical router. Please see the > @@ -4244,7 +4330,7 @@ next; > </li> > </ul> > > - <h3>Ingress Table 16: ECMP handling for router policies</h3> > + <h3>Ingress Table 17: ECMP handling for router policies</h3> > <p> > This table handles the ECMP for the router policies configured > with multiple nexthops. > @@ -4293,7 +4379,84 @@ outport = <var>P</var> > </li> > </ul> > > - <h3>Ingress Table 17: ARP/ND Resolution</h3> > + <h3>Ingress Table 18: DHCP Relay Response Check</h3> > + <p> > + This stage process the DHCP response packets coming from the DHCP > server. > + </p> > + > + <ul> > + <li> > + <p> > + A priority 100 logical flow is added for each logical router port > + configured with DHCP relay that matches <code>ip4.src</code> is > + DHCP server ip and <code>ip4.dst</code> is lrp IP and > + <code>ip4.frag == 0</code> and <code>udp.src == 67</code> and > + <code>udp.dst == 67</code> and applies <code>dhcp_relay_resp_chk > + </code> action. Original destination ip is stored in reg2. > + </p> > + > + <pre> > + reg9[8] = dhcp_relay_resp_chk(<var>lrp_ip</var>, > + <var>dhcp_server_ip</var>);next > + </pre> > + > + <p> > + if action is successful then, dest mac and dest IP addresses are > + updated in the packet and stores 1 into reg9[8] else stores 0 into > + reg9[8]. > + </p> > + </li> > + > + <li> > + A priority-0 flow that matches all packets to advance to the next > + table. > + </li> > + </ul> > + > + <h3>Ingress Table 19: DHCP Relay Response</h3> > + <p> > + This stage process the DHCP response packets on which > + <code>dhcp_relay_resp_chk</code> action is applied in the previous > stage. > + </p> > + <ul> > + <li> > + <p> > + A priority 100 logical flow is added for each logical router > port > + configured with DHCP relay that matches <code>ip4.src</code> is > + DHCP server ip and <code>reg2</code> is lrp IP and > + <code>udp.src == 67</code> and <code>udp.dst == 67</code> > + and <code>reg9[8] == 1</code> and applies following actions. If > + <code>reg9[8]</code> is set to 1 then, > + <code>dhcp_relay_resp_chk</code> was successful. > + </p> > + > + <pre> > +ip4.src = <var>lrp ip</var>; > +udp.dst = 68; > +outport = <var>lrp port</var>; > +output; > + </pre> > + </li> > + > + <li> > + <p> > + A priority 1 logical flow is added for the logical router port > + on which DHCP relay is enabled that matches <code>ip4.src</code> > + is DHCP server ip and <code>reg2</code> is lrp IP and > + <code>udp.src == 67</code> and <code>udp.dst == 67</code> > + and <code>reg9[8] == 0</code> and drops the packet. If > + <code>reg9[8]</code> is set to 0 then, > + <code>dhcp_relay_resp_chk</code> was unsuccessful. > + </p> > + </li> > + > + <li> > + A priority-0 flow that matches all packets to advance to the next > + table. > + </li> > + </ul> > + > + <h3>Ingress Table 20: ARP/ND Resolution</h3> > > <p> > Any packet that reaches this table is an IP packet whose next-hop > @@ -4507,7 +4670,7 @@ outport = <var>P</var> > > </ul> > > - <h3>Ingress Table 18: Check packet length</h3> > + <h3>Ingress Table 21: Check packet length</h3> > > <p> > For distributed logical routers or gateway routers with gateway > @@ -4544,7 +4707,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); > next; > and advances to the next table. > </p> > > - <h3>Ingress Table 19: Handle larger packets</h3> > + <h3>Ingress Table 22: Handle larger packets</h3> > > <p> > For distributed logical routers or gateway routers with gateway port > @@ -4607,7 +4770,7 @@ icmp6 { > and advances to the next table. > </p> > > - <h3>Ingress Table 20: Gateway Redirect</h3> > + <h3>Ingress Table 23: Gateway Redirect</h3> > > <p> > For distributed logical routers where one or more of the logical > router > @@ -4691,7 +4854,7 @@ icmp6 { > </li> > </ul> > > - <h3>Ingress Table 21: ARP Request</h3> > + <h3>Ingress Table 24: ARP Request</h3> > > <p> > In the common case where the Ethernet destination has been > resolved, this > diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema > index a9c5b7af5..10ce50b25 100644 > --- a/ovn-nb.ovsschema > +++ b/ovn-nb.ovsschema > @@ -1,7 +1,7 @@ > { > "name": "OVN_Northbound", > - "version": "7.3.0", > - "cksum": "3546526738 34483", > + "version": "7.3.1", > + "cksum": "3899022625 35372", > "tables": { > "NB_Global": { > "columns": { > @@ -436,6 +436,11 @@ > "ipv6_prefix": {"type": {"key": "string", > "min": 0, > "max": "unlimited"}}, > + "dhcp_relay": {"type": {"key": {"type": "uuid", > + "refTable": "DHCP_Relay", > + "refType": "strong"}, > + "min": 0, > + "max": 1}}, > "external_ids": { > "type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}, > @@ -534,6 +539,18 @@ > "type": {"key": "string", "value": "string", > "min": 0, "max": "unlimited"}}}, > "isRoot": true}, > + "DHCP_Relay": { > + "columns": { > + "name": {"type": "string"}, > + "servers": {"type": {"key": "string", > + "min": 0, > + "max": 1}}, > + "options": {"type": {"key": "string", "value": "string", > + "min": 0, "max": "unlimited"}}, > + "external_ids": { > + "type": {"key": "string", "value": "string", > + "min": 0, "max": "unlimited"}}}, > + "isRoot": true}, > "Connection": { > "columns": { > "target": {"type": "string"}, > diff --git a/ovn-nb.xml b/ovn-nb.xml > index b652046a7..5cb6ba640 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -703,6 +703,13 @@ > </ul> > </column> > > + <column name="other_config" key="dhcp_relay_port"> > + If set to the name of logical switch port of type > <code>router</code> > + then, DHCP Relay is enabled for this logical switch provided the > + corresponding <ref table="Logical_Router_Port"/> has DHCP Relay > + configured. > + </column> > + > <column name="other_config" key="mac_only" type='{"type": > "boolean"}'> > Value used to request to assign L2 address only if neither subnet > nor ipv6_prefix are specified > @@ -3066,6 +3073,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 > @@ -4379,6 +4391,33 @@ or > </group> > </table> > > + <table name="DHCP_Relay" title="DHCP Relay"> > + <p> > + OVN implements native DHCPv4 relay support which caters to the > common > + use case of relaying the DHCP requests to external DHCP server. > + </p> > + <column name="name"> > + <p> > + A name for the DHCP Relay. > + </p> > + </column> > + <column name="servers"> > + <p> > + The DHCPv4 server IP address. > + </p> > + </column> > + <column name="options"> > + <p> > + Future purpose. > + </p> > + </column> > + <group title="Common Columns"> > + <column name="external_ids"> > + See <em>External IDs</em> at the beginning of this document. > + </column> > + </group> > + </table> > + > <table name="Connection" title="OVSDB client connections."> > <p> > Configuration for a database connection to an Open vSwitch database > diff --git a/tests/atlocal.in b/tests/atlocal.in > index 63d891b89..32d1c374e 100644 > --- a/tests/atlocal.in > +++ b/tests/atlocal.in > @@ -187,6 +187,9 @@ fi > # Set HAVE_DHCPD > find_command dhcpd > > +# Set HAVE_DHCLIENT > +find_command dhclient > + > # Set HAVE_BFDD_BEACON > find_command bfdd-beacon > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index dcc29ffa8..7267c7017 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -12191,6 +12191,44 @@ check_row_count nb:QoS 0 > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD_NO_HV([ > +AT_SETUP([check DHCP RELAY]) > +ovn_start NORTHD_TYPE > + > +check ovn-nbctl ls-add ls0 > +check ovn-nbctl lsp-add ls0 ls0-port1 > +check ovn-nbctl lsp-set-addresses ls0-port1 02:00:00:00:00:10 > +check ovn-nbctl lr-add lr0 > +check ovn-nbctl lrp-add lr0 lrp1 02:00:00:00:00:01 192.168.1.1/24 > +check ovn-nbctl lsp-add ls0 lrp1-attachment > +check ovn-nbctl lsp-set-type lrp1-attachment router > +check ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02 > +check ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1 > +check ovn-nbctl lrp-add lr0 lrp-ext 02:00:00:00:00:02 192.168.2.1/24 > + > +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) > +check ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay > +check ovn-nbctl set Logical_Switch ls0 > other_config:dhcp_relay_port=lrp1-attachment > + > +check ovn-nbctl --wait=sb sync > + > +ovn-sbctl lflow-list > lflows > +AT_CAPTURE_FILE([lflows]) > + > +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed 's/table=../table=??/'], > [0], [dnl > + table=??(lr_in_ip_input ), priority=110 , match=(inport == "lrp1" > && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && ip.frag == 0 && > udp.src == 68 && udp.dst == 67), action=(reg9[[7]] = > dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);next; /* DHCP_RELAY_REQ */) > + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == > 172.16.1.1 && ip4.dst == 192.168.1.1 && ip.frag == 0 && udp.src == 67 && > udp.dst == 67), action=(next;/* DHCP_RELAY_RESP */) > + table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" > && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && > udp.dst == 67 && reg9[[7]]), > action=(ip4.src=192.168.1.1;ip4.dst=172.16.1.1;udp.src=67;next; /* > DHCP_RELAY_REQ */) > + table=??(lr_in_dhcp_relay_req), priority=1 , match=(inport == "lrp1" > && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && > udp.dst == 67 && reg9[[7]] == 0), action=(drop; /* DHCP_RELAY_REQ */) > + table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src == > 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), > action=(reg2 = ip4.dst;reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, > 172.16.1.1);next;/* DHCP_RELAY_RESP */) > + table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == > 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && > reg9[[8]]), action=(ip4.src=192.168.1.1;udp.dst=68;outport="lrp1";output; > /* DHCP_RELAY_RESP */) > + table=??(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src == > 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && > reg9[[8]] == 0), action=(drop; /* DHCP_RELAY_RESP */) > + table=??(ls_in_l2_lkup ), priority=100 , match=(inport == > "ls0-port1" && eth.src == 02:00:00:00:00:10 && ip4.src == 0.0.0.0 && > ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), > action=(eth.dst=02:00:00:00:00:01;outport="lrp1-attachment";next;/* > DHCP_RELAY_REQ */) > +]) > + > +AT_CLEANUP > +]) > + > AT_SETUP([NB_Global and SB_Global incremental processing]) > > ovn_start > diff --git a/tests/ovn.at b/tests/ovn.at > index 1ad4159cf..395bcf142 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -34273,7 +34273,7 @@ check ovn-nbctl set nb_global . > options:use_common_zone="true" > check ovn-nbctl --wait=hv sync > # Use constants so that if tables or registers change, this test can > # be updated easily. > -DNAT_TABLE=15 > +DNAT_TABLE=16 > SNAT_TABLE=45 > DNAT_ZONE_REG="NXM_NX_REG11[[0..15]]" > SNAT_ZONE_REG="NXM_NX_REG12[[0..15]]" > @@ -37806,3 +37806,225 @@ OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows > br-int table=0 |grep priority=1 > OVN_CLEANUP([hv1]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([DHCP RELAY]) > +ovn_start > +net_add n1 > + > +AT_CHECK([ovn-nbctl ls-add ls0]) > +AT_CHECK([ovn-nbctl lsp-add ls0 vif0]) > +AT_CHECK([ovn-nbctl lsp-set-addresses vif0 "50:54:00:00:00:10"]) > +AT_CHECK([ovn-nbctl lsp-add ls0 lrp1-attachment]) > +AT_CHECK([ovn-nbctl lsp-set-type lrp1-attachment router]) > +AT_CHECK([ovn-nbctl lsp-set-addresses lrp1-attachment 50:54:00:00:00:01]) > +AT_CHECK([ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1]) > + > +AT_CHECK([ovn-nbctl lr-add lr0]) > +AT_CHECK([ovn-nbctl lrp-add lr0 lrp1 50:54:00:00:00:01 192.168.1.1/24]) > +AT_CHECK([ovn-nbctl lrp-add lr0 lrp2 50:54:00:00:00:02 172.16.1.254/24]) > + > +AT_CHECK([ovn-nbctl ls-add ls-ext]) > +AT_CHECK([ovn-nbctl lsp-add ls-ext lrp2-attachment]) > +AT_CHECK([ovn-nbctl lsp-set-type lrp2-attachment router]) > +AT_CHECK([ovn-nbctl lsp-set-addresses lrp2-attachment 50:54:00:00:00:02]) > +AT_CHECK([ovn-nbctl lsp-set-options lrp2-attachment router-port=lrp2]) > +AT_CHECK([ovn-nbctl lsp-add ls-ext ln_port]) > +AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) > +AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) > +AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) > + > +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) > +AT_CHECK([ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay]) > +AT_CHECK([ovn-nbctl set Logical_Switch ls0 > other_config:dhcp_relay_port=lrp1-attachment]) > + > +sim_add hv1 > +as hv1 > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > +ovs-vsctl -- add-port br-int vif0 -- \ > + set interface vif0 external-ids:iface-id=vif0 \ > + options:tx_pcap=hv1/vif0-tx.pcap \ > + options:rxq_pcap=hv1/vif0-rx.pcap \ > + ofport-request=1 > +ovs-vsctl -- add-port br-phys ext0 -- \ > + set interface ext0 \ > + options:tx_pcap=hv1/ext0-tx.pcap \ > + options:rxq_pcap=hv1/ext0-rx.pcap \ > + ofport-request=2 > + > +ovs-vsctl set open . external_ids:ovn-bridge-mappings=physnet1:br-phys > + > +wait_for_ports_up > +AT_CHECK([ovn-nbctl --wait=hv sync]) > + > +send_dhcp_packet() { > + src_mac=${1} > + src_ip=${2} > + dst_mac=${3} > + dst_ip=${4} > + op_code=${5} > + msg_type=${6} > + yiaddr=$7 > + giaddr=${8} > + sid=${9} > + iface=${10} > + #echo "ARGS: ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10}" > + echo "ARGS: $@" > + if [[ $op_code == "01" ]]; then > + ip_len=0111 > + udp_len=00fd > + src_port=0044 > + else > + ip_len=011d > + udp_len=0109 > + src_port=0043 > + fi > + flags=0000 > + > + local > pkt=${dst_mac}${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip} > + # udp header and dhcp header > + pkt=${pkt}${src_port}0043${udp_len}0000 > + > pkt=${pkt}${op_code}0106006359aa760000${flags}00000000${yiaddr}00000000${giaddr}${src_mac} > + # client hardware padding > + pkt=${pkt}00000000000000000000 > + # server hostname > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + # boot file name > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + > pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 > + # dhcp magic cookie > + pkt=${pkt}63825363 > + # dhcp message type > + pkt=${pkt}3501${msg_type} > + # dhcp server identifier and subnet mask options > + if [[ $op_code == "02" ]]; then > + pkt=${pkt}3604${sid} > + pkt=${pkt}0104ffffff00 > + fi > + # dhcp pad option > + pkt=${pkt}00 > + # dhcp end option > + pkt=${pkt}ff > + > + tcpdump_hex "-- sending DHCP pkt on hv1-$iface" $pkt > + > + ovs-appctl netdev-dummy/receive $iface $pkt > +} > + > +ovn-sbctl dump-flows > lflows > +AT_CAPTURE_FILE([lflows]) > + > +# Get the OF table numbers > +dhcp_relay_req_table=$(ovn-debug lflow-stage-to-oftable > lr_in_dhcp_relay_req) > +dhcp_relay_resp_table=$(ovn-debug lflow-stage-to-oftable > lr_in_dhcp_relay_resp) > + > +# ==================================================== > +# Send DHCP valid discovery > +src_mac="505400000010" > +src_ip=`ip_to_hex 0.0.0.0` > +dst_mac="ffffffffffff" > +dst_ip=`ip_to_hex 255.255.255.255` > +yiaddr=`ip_to_hex 0.0.0.0` > +giaddr=`ip_to_hex 0.0.0.0` > +sid=$src_ip > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr > $sid vif0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_req_table > > pflows1_dhcp_relay_req > +AT_CAPTURE_FILE([pflows1_dhcp_relay_req]) > + > +AT_CHECK([cat pflows1_dhcp_relay_req | grep -v NXST | grep > 255.255.255.255 | grep resubmit | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=1 > +]) > + > +# ==================================================== > +# Send DHCP discovery with giaddr set > +giaddr=`ip_to_hex 192.168.1.1` > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr > $sid vif0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_req_table > > pflows2_dhcp_relay_req > +AT_CAPTURE_FILE([pflows2_dhcp_relay_req]) > + > +AT_CHECK([cat pflows2_dhcp_relay_req | grep -v NXST | grep > 255.255.255.255 | grep drop | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=1 > +]) > + > +# ==================================================== > +# Send DHCP valid offer > +src_mac="50540000001f" > +src_ip=`ip_to_hex 172.16.1.1` > +dst_mac="505400000002" > +dst_ip=`ip_to_hex 192.168.1.1` > +yiaddr=`ip_to_hex 192.168.1.10` > +giaddr=`ip_to_hex 192.168.1.1` > +sid=$src_ip > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr > $sid ext0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > > pflows1_dhcp_relay_resp > +AT_CAPTURE_FILE([pflows1_dhcp_relay_resp]) > + > +AT_CHECK([cat pflows1_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | > grep resubmit | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=1 > +]) > +# ==================================================== > +# Send DHCP offer with incorrect giaddr > +giaddr=`ip_to_hex 192.168.1.10` > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr > $sid ext0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > > pflows2_dhcp_relay_resp > +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) > + > +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | > grep drop | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=1 > +]) > + > +giaddr=`ip_to_hex 192.168.1.1` > + > +# ==================================================== > +# Send DHCP offer with yiaddr outside of the subnet > +yiaddr=`ip_to_hex 192.168.2.10` > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr > $sid ext0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > > pflows2_dhcp_relay_resp > +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) > + > +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | > grep drop | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=2 > +]) > + > +yiaddr=`ip_to_hex 192.168.1.10` > + > +# ==================================================== > +# Send DHCP offer with differnt server identifier > +sid=`ip_to_hex 172.16.1.100` > +# send packet > +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr > $sid ext0 > + > +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > > pflows2_dhcp_relay_resp > +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) > + > +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | > grep drop | > +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl > +n_packets=3 > +]) > + > +sid=`ip_to_hex 172.16.1.1` > + > +OVN_CLEANUP([hv1 > +/WARN\|DHCP_RELAY/d > +]) > +AT_CLEANUP > +]) > diff --git a/tests/system-ovn.at b/tests/system-ovn.at > index 5848f3901..b9f731396 100644 > --- a/tests/system-ovn.at > +++ b/tests/system-ovn.at > @@ -12512,3 +12512,151 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query > port patch-.*/d > /connection dropped.*/d"]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([DHCP RELAY]) > +AT_SKIP_IF([test $HAVE_DHCPD = no]) > +AT_SKIP_IF([test $HAVE_DHCLIENT = no]) > +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) > +ovn_start > +OVS_TRAFFIC_VSWITCHD_START() > + > +ADD_BR([br-int]) > +ADD_BR([br-ext]) > + > +ovs-ofctl add-flow br-ext action=normal > +# Set external-ids in br-int needed for ovn-controller > +ovs-vsctl \ > + -- set Open_vSwitch . external-ids:system-id=hv1 \ > + -- set Open_vSwitch . > external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ > + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ > + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ > + -- set bridge br-int fail-mode=secure > other-config:disable-in-band=true > + > +# Start ovn-controller > +start_daemon ovn-controller > + > +ADD_NAMESPACES(sw01) > +ADD_VETH(sw01, sw01, br-int, "0", "f0:00:00:01:02:03") > +ADD_NAMESPACES(sw11) > +ADD_VETH(sw11, sw11, br-int, "0", "f0:00:00:02:02:03") > +ADD_NAMESPACES(server) > +ADD_VETH(s1, server, br-ext, "172.16.1.1/24", "f0:00:00:01:02:05", \ > + "172.16.1.254") > + > +check ovn-nbctl lr-add R1 > + > +check ovn-nbctl ls-add sw0 > +check ovn-nbctl ls-add sw1 > +check ovn-nbctl ls-add sw-ext > + > +check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24 > +check ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24 > +check ovn-nbctl lrp-add R1 rp-ext 00:00:02:01:02:03 172.16.1.254/24 > + > +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) > +check ovn-nbctl set Logical_Router_port rp-sw0 dhcp_relay=$dhcp_relay > +check ovn-nbctl set Logical_Router_port rp-sw1 dhcp_relay=$dhcp_relay > +check ovn-nbctl lrp-set-gateway-chassis rp-ext hv1 > + > +check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \ > + type=router options:router-port=rp-sw0 \ > + -- lsp-set-addresses sw0-rp router > +check ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \ > + type=router options:router-port=rp-sw1 \ > + -- lsp-set-addresses sw1-rp router > + > +check ovn-nbctl set Logical_Switch sw0 other_config:dhcp_relay_port=sw0-rp > +check ovn-nbctl set Logical_Switch sw1 other_config:dhcp_relay_port=sw1-rp > + > +check ovn-nbctl lsp-add sw-ext ext-rp -- set Logical_Switch_Port ext-rp \ > + type=router options:router-port=rp-ext \ > + -- lsp-set-addresses ext-rp router > +check ovn-nbctl lsp-add sw-ext lnet \ > + -- lsp-set-addresses lnet unknown \ > + -- lsp-set-type lnet localnet \ > + -- lsp-set-options lnet network_name=phynet > + > +check ovn-nbctl lsp-add sw0 sw01 \ > + -- lsp-set-addresses sw01 "f0:00:00:01:02:03" > + > +check ovn-nbctl lsp-add sw1 sw11 \ > + -- lsp-set-addresses sw11 "f0:00:00:02:02:03" > + > +AT_CHECK([ovs-vsctl set Open_vSwitch . > external-ids:ovn-bridge-mappings=phynet:br-ext]) > + > +OVN_POPULATE_ARP > + > +check ovn-nbctl --wait=hv sync > + > +DHCP_TEST_DIR="/tmp/dhcp-test" > +rm -rf $DHCP_TEST_DIR > +mkdir $DHCP_TEST_DIR > +cat > $DHCP_TEST_DIR/dhcpd.conf <<EOF > +subnet 172.16.1.0 netmask 255.255.255.0 { > +} > +subnet 192.168.1.0 netmask 255.255.255.0 { > + range 192.168.1.10 192.168.1.10; > + option routers 192.168.1.1; > + option broadcast-address 192.168.1.255; > + default-lease-time 60; > + max-lease-time 120; > +} > +subnet 192.168.2.0 netmask 255.255.255.0 { > + range 192.168.2.10 192.168.2.10; > + option routers 192.168.2.1; > + option broadcast-address 192.168.2.255; > + default-lease-time 60; > + max-lease-time 120; > +} > +EOF > +cat > $DHCP_TEST_DIR/dhclien.conf <<EOF > +timeout 2 > +EOF > + > +touch $DHCP_TEST_DIR/dhcpd.leases > +chown root:dhcpd $DHCP_TEST_DIR $DHCP_TEST_DIR/dhcpd.leases > +chmod 775 $DHCP_TEST_DIR > +chmod 664 $DHCP_TEST_DIR/dhcpd.leases > + > + > +NETNS_DAEMONIZE([server], [dhcpd -4 -f -cf $DHCP_TEST_DIR/dhcpd.conf s1 > > dhcpd.log 2>&1], [dhcpd.pid]) > + > +NS_CHECK_EXEC([server], [tcpdump -l -nvv -i s1 udp > pkt.pcap > 2>tcpdump_err &]) > +OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) > +on_exit 'kill $(pidof tcpdump)' > + > +NS_CHECK_EXEC([sw01], [dhclient -1 -q -lf > $DHCP_TEST_DIR/dhclient-sw01.lease -pf $DHCP_TEST_DIR/dhclient-sw01.pid -cf > $DHCP_TEST_DIR/dhclien.conf sw01]) > +NS_CHECK_EXEC([sw11], [dhclient -1 -q -lf > $DHCP_TEST_DIR/dhclient-sw11.lease -pf $DHCP_TEST_DIR/dhclient-sw11.pid -cf > $DHCP_TEST_DIR/dhclien.conf sw11]) > + > +OVS_WAIT_UNTIL([ > + total_pkts=$(cat pkt.pcap | wc -l) > + test ${total_pkts} -ge 8 > +]) > + > +on_exit 'kill `cat $DHCP_TEST_DIR/dhclient-sw01.pid` && > +kill `cat $DHCP_TEST_DIR/dhclient-sw11.pid` && rm -rf $DHCP_TEST_DIR' > + > +NS_CHECK_EXEC([sw01], [ip addr show sw01 | grep -oP > '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl > +192.168.1.10 > +]) > +NS_CHECK_EXEC([sw11], [ip addr show sw11 | grep -oP > '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl > +192.168.2.10 > +]) > +OVS_APP_EXIT_AND_WAIT([ovn-controller]) > + > +as ovn-sb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as ovn-nb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as northd > +OVS_APP_EXIT_AND_WAIT([ovn-northd]) > + > +as > +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d > +/failed to query port patch-.*/d > +/.*terminating with signal 15.*/d"]) > +AT_CLEANUP > +]) > -- > 2.36.6 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > >
diff --git a/northd/northd.c b/northd/northd.c index 331d9c267..045c3576b 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -91,7 +91,6 @@ static bool use_ct_inv_match = true; static bool default_acl_drop; #define MAX_OVN_TAGS 4096 - /* Due to various hard-coded priorities need to implement ACLs, the * northbound database supports a smaller range of ACL priorities than @@ -153,6 +152,8 @@ static bool default_acl_drop; #define REGBIT_LOOKUP_NEIGHBOR_IP_RESULT "reg9[3]" #define REGBIT_DST_NAT_IP_LOCAL "reg9[4]" #define REGBIT_KNOWN_LB_SESSION "reg9[6]" +#define REGBIT_DHCP_RELAY_REQ_CHK "reg9[7]" +#define REGBIT_DHCP_RELAY_RESP_CHK "reg9[8]" /* Register to store the eth address associated to a router port for packets * received in S_ROUTER_IN_ADMISSION. @@ -168,6 +169,7 @@ static bool default_acl_drop; #define REG_NEXT_HOP_IPV6 "xxreg0" #define REG_SRC_IPV4 "reg1" #define REG_SRC_IPV6 "xxreg1" +#define REG_DHCP_RELAY_DIP_IPV4 "reg2" #define REG_ROUTE_TABLE_ID "reg7" /* Register used to store backend ipv6 address @@ -232,7 +234,7 @@ static bool default_acl_drop; * | R1 | SRC_IPV4 for ARP-REQ | 0 | | R | | * | | (>= IP_INPUT) | | | E | NEXT_HOP_IPV6 (>= DEFRAG ) | * +-----+---------------------------+---+-----------------+ G | | - * | R2 | UNUSED | X | | 0 | | + * | R2 REG_DHCP_RELAY_DIP_IPV4 | X | | 0 | | * | | | R | | | | * +-----+---------------------------+ E | UNUSED | | | * | R3 | UNUSED | G | | | | @@ -259,7 +261,9 @@ static bool default_acl_drop; * | | EGRESS_LOOPBACK/ | G | UNUSED | * | R9 | PKT_LARGER/ | 4 | | * | | LOOKUP_NEIGHBOR_RESULT/ | | | - * | | SKIP_LOOKUP_NEIGHBOR} | | | + * | | SKIP_LOOKUP_NEIGHBOR/ | | | + * | |REGBIT_DHCP_RELAY_REQ_CHK/ | | | + * | |REGBIT_DHCP_RELAY_RESP_CHK}| | | * | | | | | * | | REG_ORIG_TP_DPORT_ROUTER | | | * | | | | | @@ -8543,6 +8547,90 @@ build_dhcpv6_options_flows(struct ovn_port *op, ds_destroy(&match); } +static const char * +ls_dhcp_relay_port(const struct ovn_datapath *od) +{ + return smap_get(&od->nbs->other_config, "dhcp_relay_port"); +} + +static void +build_lswitch_dhcp_relay_flows(struct ovn_port *op, + const struct hmap *ls_ports, + struct lflow_table *lflows, + struct ds *match, + struct ds *actions) +{ + if (op->nbrp || !op->nbsp) { + return; + } + + /* consider only ports attached to VMs */ + if (strcmp(op->nbsp->type, "")) { + return; + } + + if (!op->od || !op->od->n_router_ports || + !op->od->nbs) { + return; + } + + /* configure dhcp relay flows only when peer router has + * relay config enabled */ + const char *dhcp_relay_port = ls_dhcp_relay_port(op->od); + if (!dhcp_relay_port) { + return; + } + + struct ovn_port *sp = ovn_port_find(ls_ports, dhcp_relay_port); + + if (!sp || !sp->nbsp || !sp->peer) { + return; + } + + struct ovn_port *rp = sp->peer; + if (!rp || !rp->nbrp || !rp->nbrp->dhcp_relay || rp->peer != sp) { + return; + } + + char *server_ip_str = NULL; + uint16_t port; + int addr_family; + struct in6_addr server_ip; + struct nbrec_dhcp_relay *dhcp_relay = rp->nbrp->dhcp_relay; + + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str, + &server_ip, &port, &addr_family)) { + return; + } + + if (server_ip_str == NULL) { + return; + } + + ds_clear(match); + ds_clear(actions); + + 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(actions, + "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(actions), + op->key, + NULL, + &op->nbsp->header_, + op->lflow_ref); + ds_clear(match); + ds_clear(actions); + free(server_ip_str); +} + static void build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op, const struct ovn_port *port, @@ -9144,6 +9232,13 @@ build_lswitch_dhcp_options_and_response(struct ovn_port *op, return; } + if (op->od && op->od->nbs + && ls_dhcp_relay_port(op->od)) { + /* Don't add the DHCP server flows if DHCP Relay is enabled on the + * logical switch. */ + return; + } + bool is_external = lsp_is_external(op->nbsp); if (is_external && (!op->od->n_localnet_ports || !op->nbsp->ha_chassis_group)) { @@ -13623,6 +13718,166 @@ build_dhcpv6_reply_flows_for_lrouter_port( } } +static void +build_dhcp_relay_flows_for_lrouter_port( + struct ovn_port *op, struct lflow_table *lflows, + struct ds *match, struct ds *actions, + struct lflow_ref *lflow_ref) +{ + if (!op->nbrp || !op->nbrp->dhcp_relay) { + return; + + } + + /* configure dhcp relay flows only when peer switch has + * relay config enabled */ + struct ovn_port *sp = op->peer; + if (!sp || !sp->nbsp || sp->peer != op || + !sp->od || !ls_dhcp_relay_port(sp->od)) { + return; + } + + struct nbrec_dhcp_relay *dhcp_relay = op->nbrp->dhcp_relay; + if (!dhcp_relay->servers) { + return; + } + + int addr_family; + /* currently not supporting custom port, + * dhcp server port is always set to 67 when installing flows */ + uint16_t port; + char *server_ip_str = NULL; + struct in6_addr server_ip; + + if (!ip_address_and_port_from_lb_key(dhcp_relay->servers, &server_ip_str, + &server_ip, &port, &addr_family)) { + return; + } + + if (server_ip_str == NULL) { + return; + } + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "inport == %s && " + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " + "ip.frag == 0 && udp.src == 68 && udp.dst == 67", + op->json_key); + ds_put_format(actions, + REGBIT_DHCP_RELAY_REQ_CHK + " = dhcp_relay_req_chk(%s, %s);" + "next; /* DHCP_RELAY_REQ */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "inport == %s && " + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " + "udp.src == 68 && udp.dst == 67 && " + REGBIT_DHCP_RELAY_REQ_CHK, + op->json_key); + ds_put_format(actions, + "ip4.src=%s;ip4.dst=%s;udp.src=67;next; /* DHCP_RELAY_REQ */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 100, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "inport == %s && " + "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && " + "udp.src == 68 && udp.dst == 67 && " + REGBIT_DHCP_RELAY_REQ_CHK" == 0", + op->json_key); + ds_put_format(actions, + "drop; /* DHCP_RELAY_REQ */"); + + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_REQ, 1, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "ip4.src == %s && ip4.dst == %s && " + "ip.frag == 0 && udp.src == 67 && udp.dst == 67", + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); + ds_put_format(actions, "next;/* DHCP_RELAY_RESP */"); + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_IP_INPUT, 110, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + ds_clear(match); + ds_clear(actions); + + 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(actions, + REG_DHCP_RELAY_DIP_IPV4" = ip4.dst;" + REGBIT_DHCP_RELAY_RESP_CHK + " = dhcp_relay_resp_chk(%s, %s);next;/* DHCP_RELAY_RESP */", + op->lrp_networks.ipv4_addrs[0].addr_s, server_ip_str); + + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, + 100, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "ip4.src == %s && " + REG_DHCP_RELAY_DIP_IPV4" == %s && " + "udp.src == 67 && udp.dst == 67 && " + REGBIT_DHCP_RELAY_RESP_CHK, + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); + ds_put_format(actions, + "ip4.src=%s;udp.dst=68;" + "outport=%s;output; /* DHCP_RELAY_RESP */", + op->lrp_networks.ipv4_addrs[0].addr_s, op->json_key); + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, + 100, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + + ds_clear(match); + ds_clear(actions); + + ds_put_format( + match, "ip4.src == %s && " + REG_DHCP_RELAY_DIP_IPV4" == %s && " + "udp.src == 67 && udp.dst == 67 && " + REGBIT_DHCP_RELAY_RESP_CHK" == 0", + server_ip_str, op->lrp_networks.ipv4_addrs[0].addr_s); + ds_put_format(actions, + "drop; /* DHCP_RELAY_RESP */"); + ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_DHCP_RELAY_RESP, + 1, + ds_cstr(match), ds_cstr(actions), + &op->nbrp->header_, lflow_ref); + ds_clear(match); + ds_clear(actions); + free(server_ip_str); +} + static void build_ipv6_input_flows_for_lrouter_port( struct ovn_port *op, struct lflow_table *lflows, @@ -14893,6 +15148,13 @@ static void build_lr_nat_defrag_and_lb_default_flows( lflow_ref); ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_REQ, 0, "1", + "next;", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP_CHK, 0, "1", + "next;", lflow_ref); + ovn_lflow_add(lflows, od, S_ROUTER_IN_DHCP_RELAY_RESP, 0, "1", + "next;", lflow_ref); + /* Send the IPv6 NS packets to next table. When ovn-controller * generates IPv6 NS (for the action - nd_ns{}), the injected @@ -15652,6 +15914,7 @@ build_lswitch_and_lrouter_iterate_by_lsp(struct ovn_port *op, build_lswitch_icmp_packet_toobig_admin_flows(op, lflows, match, actions); build_lswitch_ip_unicast_lookup(op, lflows, actions, match); + build_lswitch_dhcp_relay_flows(op, ls_ports, lflows, match, actions); /* Build Logical Router Flows. */ build_arp_resolve_flows_for_lsp(op, lflows, lr_ports, match, actions); @@ -15681,6 +15944,8 @@ build_lswitch_and_lrouter_iterate_by_lrp(struct ovn_port *op, op->lflow_ref); build_dhcpv6_reply_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, op->lflow_ref); + build_dhcp_relay_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, + &lsi->actions, op->lflow_ref); build_ipv6_input_flows_for_lrouter_port(op, lsi->lflows, &lsi->match, &lsi->actions, lsi->meter_groups, diff --git a/northd/northd.h b/northd/northd.h index 18cad5234..940926945 100644 --- a/northd/northd.h +++ b/northd/northd.h @@ -441,24 +441,29 @@ enum ovn_stage { PIPELINE_STAGE(ROUTER, IN, LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \ PIPELINE_STAGE(ROUTER, IN, LEARN_NEIGHBOR, 2, "lr_in_learn_neighbor") \ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 3, "lr_in_ip_input") \ - PIPELINE_STAGE(ROUTER, IN, UNSNAT, 4, "lr_in_unsnat") \ - PIPELINE_STAGE(ROUTER, IN, DEFRAG, 5, "lr_in_defrag") \ - PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 6, "lr_in_lb_aff_check") \ - PIPELINE_STAGE(ROUTER, IN, DNAT, 7, "lr_in_dnat") \ - PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 8, "lr_in_lb_aff_learn") \ - PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 9, "lr_in_ecmp_stateful") \ - PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 10, "lr_in_nd_ra_options") \ - PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 11, "lr_in_nd_ra_response") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 12, "lr_in_ip_routing_pre") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 13, "lr_in_ip_routing") \ - PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 14, "lr_in_ip_routing_ecmp") \ - PIPELINE_STAGE(ROUTER, IN, POLICY, 15, "lr_in_policy") \ - PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 16, "lr_in_policy_ecmp") \ - PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 17, "lr_in_arp_resolve") \ - PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 18, "lr_in_chk_pkt_len") \ - PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 19, "lr_in_larger_pkts") \ - PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 20, "lr_in_gw_redirect") \ - PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 21, "lr_in_arp_request") \ + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_REQ, 4, "lr_in_dhcp_relay_req") \ + PIPELINE_STAGE(ROUTER, IN, UNSNAT, 5, "lr_in_unsnat") \ + PIPELINE_STAGE(ROUTER, IN, DEFRAG, 6, "lr_in_defrag") \ + PIPELINE_STAGE(ROUTER, IN, LB_AFF_CHECK, 7, "lr_in_lb_aff_check") \ + PIPELINE_STAGE(ROUTER, IN, DNAT, 8, "lr_in_dnat") \ + PIPELINE_STAGE(ROUTER, IN, LB_AFF_LEARN, 9, "lr_in_lb_aff_learn") \ + PIPELINE_STAGE(ROUTER, IN, ECMP_STATEFUL, 10, "lr_in_ecmp_stateful") \ + PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 11, "lr_in_nd_ra_options") \ + PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 12, "lr_in_nd_ra_response") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_PRE, 13, "lr_in_ip_routing_pre") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 14, "lr_in_ip_routing") \ + PIPELINE_STAGE(ROUTER, IN, IP_ROUTING_ECMP, 15, "lr_in_ip_routing_ecmp") \ + PIPELINE_STAGE(ROUTER, IN, POLICY, 16, "lr_in_policy") \ + PIPELINE_STAGE(ROUTER, IN, POLICY_ECMP, 17, "lr_in_policy_ecmp") \ + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP_CHK, 18, \ + "lr_in_dhcp_relay_resp_chk") \ + PIPELINE_STAGE(ROUTER, IN, DHCP_RELAY_RESP, 19, \ + "lr_in_dhcp_relay_resp") \ + PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 20, "lr_in_arp_resolve") \ + PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN, 21, "lr_in_chk_pkt_len") \ + PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 22, "lr_in_larger_pkts") \ + PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 23, "lr_in_gw_redirect") \ + PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 24, "lr_in_arp_request") \ \ /* Logical router egress stages. */ \ PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL, 0, \ diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml index b14a30285..1e7fb3888 100644 --- a/northd/ovn-northd.8.xml +++ b/northd/ovn-northd.8.xml @@ -1934,6 +1934,12 @@ output; logical switch. </li> + <li> + A priority-100 flow that forwards all DHCP broadcast packets coming + from VIFs to the logical router port's MAC when DHCP relay is enabled + on the logical switch. + </li> + <li> Priority-90 flows for transit switches that forward registered IP multicast traffic to their corresponding multicast group , which @@ -2864,6 +2870,44 @@ icmp6_error { </p> </li> + <li> + <p> + For each logical router port configured with DHCP relay the + following priority-110 flows are added to manage the DHCP relay + traffic: + + <ul> + <li> + <p> + if <code>inport</code> is lrp and <code>ip4.src == 0.0.0.0 + </code> and <code>ip4.dst == 255.255.255.255</code> and + <code>ip4.frag == 0 </code> and <code>udp.src == 68</code> + and <code>udp.dst == 67</code>, the <code>dhcp_relay_req_chk + </code> action is executed. + </p> + + <pre> + reg9[7] = dhcp_relay_req_chk(<var>lrp_ip</var>, + <var>dhcp_server_ip</var>);next + </pre> + + <p> + if action is successful then, GIADDR in the dhcp header is + updated with lrp ip and stores 1 into reg9[7] else stores 0 + into reg9[7]. + </p> + </li> + + <li> + if <code>ip4.src</code> is DHCP server ip and <code>ip4.dst + </code> is lrp IP and <code>udp.src == 67</code> and + <code>udp.dst == 67</code>, the packet is advanced to the next + pipeline stage. + </li> + </ul> + </p> + </li> + <li> <p> L3 admission control: Priority-120 flows allows IGMP and MLD packets @@ -3328,8 +3372,50 @@ icmp6 { </ul> + <h3>Ingress Table 4: DHCP Relay Request</h3> + <p> + This stage process the DHCP request packets on which + <code>dhcp_relay_req_chk</code> action is applied in the IP input stage. + </p> + <ul> + <li> + <p> + A priority-100 logical flow is added for each logical router port + configured with DHCP relay that matches <code>inport</code> is lrp + and <code>ip4.src == 0.0.0.0</code> and + <code>ip4.dst == 255.255.255.255</code> and <code>udp.src == 68 + </code> and <code>udp.dst == 67</code> and <code>reg9[7] == 1</code> + and applies following actions. If <code>reg9[7]</code> is set to 1 + then, <code>dhcp_relay_req_chk</code> action was successful. + </p> + + <pre> +ip4.src=<var>lrp ip</var>; +ip4.dst=<var>dhcp server ip</var>; +udp.src = 67; +next; + </pre> + </li> + + <li> + <p> + A priority-1 logical flow is added for each logical router port + configured with DHCP relay that matches <code>inport</code> is lrp + and <code>ip4.src == 0.0.0.0</code> and + <code>ip4.dst == 255.255.255.255</code> and <code>udp.src == 68 + </code> and <code>udp.dst == 67</code> and <code>reg9[7] == 0</code> + and drops the packet. If <code>reg9[7]</code> is set to 0 then, + <code>dhcp_relay_req_chk</code> action was unsuccessful. + </p> + </li> + + <li> + A priority-0 flow that matches all packets to advance to the next + table. + </li> + </ul> - <h3>Ingress Table 4: UNSNAT</h3> + <h3>Ingress Table 5: UNSNAT</h3> <p> This is for already established connections' reverse traffic. @@ -3338,7 +3424,7 @@ icmp6 { unSNATted here. </p> - <p>Ingress Table 4: UNSNAT on Gateway and Distributed Routers</p> + <p>Ingress Table 5: UNSNAT on Gateway and Distributed Routers</p> <ul> <li> <p> @@ -3365,7 +3451,7 @@ icmp6 { </li> </ul> - <p>Ingress Table 4: UNSNAT on Gateway Routers</p> + <p>Ingress Table 5: UNSNAT on Gateway Routers</p> <ul> <li> @@ -3414,7 +3500,7 @@ icmp6 { </li> </ul> - <p>Ingress Table 4: UNSNAT on Distributed Routers</p> + <p>Ingress Table 5: UNSNAT on Distributed Routers</p> <ul> <li> @@ -3461,7 +3547,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 5: DEFRAG</h3> + <h3>Ingress Table 6: DEFRAG</h3> <p> This is to send packets to connection tracker for tracking and @@ -3504,7 +3590,7 @@ icmp6 { this allows potentially related ICMP traffic to pass through CT. </p> - <h3>Ingress Table 6: Load balancing affinity check</h3> + <h3>Ingress Table 7: Load balancing affinity check</h3> <p> Load balancing affinity check table contains the following @@ -3531,7 +3617,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 7: DNAT</h3> + <h3>Ingress Table 8: DNAT</h3> <p> Packets enter the pipeline with destination IP address that needs to @@ -3539,7 +3625,7 @@ icmp6 { in the reverse direction needs to be unDNATed. </p> - <p>Ingress Table 7: Load balancing DNAT rules</p> + <p>Ingress Table 8: Load balancing DNAT rules</p> <p> Following load balancing DNAT flows are added for Gateway router or @@ -3660,7 +3746,7 @@ icmp6 { </li> </ul> - <p>Ingress Table 7: DNAT on Gateway Routers</p> + <p>Ingress Table 8: DNAT on Gateway Routers</p> <ul> <li> @@ -3702,7 +3788,7 @@ icmp6 { </li> </ul> - <p>Ingress Table 7: DNAT on Distributed Routers</p> + <p>Ingress Table 8: DNAT on Distributed Routers</p> <p> On distributed routers, the DNAT table only handles packets @@ -3757,7 +3843,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 8: Load balancing affinity learn</h3> + <h3>Ingress Table 9: Load balancing affinity learn</h3> <p> Load balancing affinity learn table contains the following @@ -3785,7 +3871,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 9: ECMP symmetric reply processing</h3> + <h3>Ingress Table 10: ECMP symmetric reply processing</h3> <ul> <li> If ECMP routes with symmetric reply are configured in the @@ -3804,7 +3890,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 10: IPv6 ND RA option processing</h3> + <h3>Ingress Table 11: IPv6 ND RA option processing</h3> <ul> <li> @@ -3834,7 +3920,7 @@ reg0[5] = put_nd_ra_opts(<var>options</var>);next; </li> </ul> - <h3>Ingress Table 11: IPv6 ND RA responder</h3> + <h3>Ingress Table 12: IPv6 ND RA responder</h3> <p> This table implements IPv6 ND RA responder for the IPv6 ND RA replies @@ -3879,7 +3965,7 @@ output; </li> </ul> - <h3>Ingress Table 12: IP Routing Pre</h3> + <h3>Ingress Table 13: IP Routing Pre</h3> <p> If a packet arrived at this table from Logical Router Port <var>P</var> @@ -3909,7 +3995,7 @@ output; </li> </ul> - <h3>Ingress Table 13: IP Routing</h3> + <h3>Ingress Table 14: IP Routing</h3> <p> A packet that arrives at this table is an IP packet that should be @@ -4115,7 +4201,7 @@ select(reg8[16..31], <var>MID1</var>, <var>MID2</var>, ...); </li> </ul> - <h3>Ingress Table 14: IP_ROUTING_ECMP</h3> + <h3>Ingress Table 15: IP_ROUTING_ECMP</h3> <p> This table implements the second part of IP routing for ECMP routes @@ -4172,7 +4258,7 @@ outport = <var>P</var>; </li> </ul> - <h3>Ingress Table 15: Router policies</h3> + <h3>Ingress Table 16: Router policies</h3> <p> This table adds flows for the logical router policies configured on the logical router. Please see the @@ -4244,7 +4330,7 @@ next; </li> </ul> - <h3>Ingress Table 16: ECMP handling for router policies</h3> + <h3>Ingress Table 17: ECMP handling for router policies</h3> <p> This table handles the ECMP for the router policies configured with multiple nexthops. @@ -4293,7 +4379,84 @@ outport = <var>P</var> </li> </ul> - <h3>Ingress Table 17: ARP/ND Resolution</h3> + <h3>Ingress Table 18: DHCP Relay Response Check</h3> + <p> + This stage process the DHCP response packets coming from the DHCP server. + </p> + + <ul> + <li> + <p> + A priority 100 logical flow is added for each logical router port + configured with DHCP relay that matches <code>ip4.src</code> is + DHCP server ip and <code>ip4.dst</code> is lrp IP and + <code>ip4.frag == 0</code> and <code>udp.src == 67</code> and + <code>udp.dst == 67</code> and applies <code>dhcp_relay_resp_chk + </code> action. Original destination ip is stored in reg2. + </p> + + <pre> + reg9[8] = dhcp_relay_resp_chk(<var>lrp_ip</var>, + <var>dhcp_server_ip</var>);next + </pre> + + <p> + if action is successful then, dest mac and dest IP addresses are + updated in the packet and stores 1 into reg9[8] else stores 0 into + reg9[8]. + </p> + </li> + + <li> + A priority-0 flow that matches all packets to advance to the next + table. + </li> + </ul> + + <h3>Ingress Table 19: DHCP Relay Response</h3> + <p> + This stage process the DHCP response packets on which + <code>dhcp_relay_resp_chk</code> action is applied in the previous stage. + </p> + <ul> + <li> + <p> + A priority 100 logical flow is added for each logical router port + configured with DHCP relay that matches <code>ip4.src</code> is + DHCP server ip and <code>reg2</code> is lrp IP and + <code>udp.src == 67</code> and <code>udp.dst == 67</code> + and <code>reg9[8] == 1</code> and applies following actions. If + <code>reg9[8]</code> is set to 1 then, + <code>dhcp_relay_resp_chk</code> was successful. + </p> + + <pre> +ip4.src = <var>lrp ip</var>; +udp.dst = 68; +outport = <var>lrp port</var>; +output; + </pre> + </li> + + <li> + <p> + A priority 1 logical flow is added for the logical router port + on which DHCP relay is enabled that matches <code>ip4.src</code> + is DHCP server ip and <code>reg2</code> is lrp IP and + <code>udp.src == 67</code> and <code>udp.dst == 67</code> + and <code>reg9[8] == 0</code> and drops the packet. If + <code>reg9[8]</code> is set to 0 then, + <code>dhcp_relay_resp_chk</code> was unsuccessful. + </p> + </li> + + <li> + A priority-0 flow that matches all packets to advance to the next + table. + </li> + </ul> + + <h3>Ingress Table 20: ARP/ND Resolution</h3> <p> Any packet that reaches this table is an IP packet whose next-hop @@ -4507,7 +4670,7 @@ outport = <var>P</var> </ul> - <h3>Ingress Table 18: Check packet length</h3> + <h3>Ingress Table 21: Check packet length</h3> <p> For distributed logical routers or gateway routers with gateway @@ -4544,7 +4707,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next; and advances to the next table. </p> - <h3>Ingress Table 19: Handle larger packets</h3> + <h3>Ingress Table 22: Handle larger packets</h3> <p> For distributed logical routers or gateway routers with gateway port @@ -4607,7 +4770,7 @@ icmp6 { and advances to the next table. </p> - <h3>Ingress Table 20: Gateway Redirect</h3> + <h3>Ingress Table 23: Gateway Redirect</h3> <p> For distributed logical routers where one or more of the logical router @@ -4691,7 +4854,7 @@ icmp6 { </li> </ul> - <h3>Ingress Table 21: ARP Request</h3> + <h3>Ingress Table 24: ARP Request</h3> <p> In the common case where the Ethernet destination has been resolved, this diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema index a9c5b7af5..10ce50b25 100644 --- a/ovn-nb.ovsschema +++ b/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "7.3.0", - "cksum": "3546526738 34483", + "version": "7.3.1", + "cksum": "3899022625 35372", "tables": { "NB_Global": { "columns": { @@ -436,6 +436,11 @@ "ipv6_prefix": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, + "dhcp_relay": {"type": {"key": {"type": "uuid", + "refTable": "DHCP_Relay", + "refType": "strong"}, + "min": 0, + "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, @@ -534,6 +539,18 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, + "DHCP_Relay": { + "columns": { + "name": {"type": "string"}, + "servers": {"type": {"key": "string", + "min": 0, + "max": 1}}, + "options": {"type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "isRoot": true}, "Connection": { "columns": { "target": {"type": "string"}, diff --git a/ovn-nb.xml b/ovn-nb.xml index b652046a7..5cb6ba640 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -703,6 +703,13 @@ </ul> </column> + <column name="other_config" key="dhcp_relay_port"> + If set to the name of logical switch port of type <code>router</code> + then, DHCP Relay is enabled for this logical switch provided the + corresponding <ref table="Logical_Router_Port"/> has DHCP Relay + configured. + </column> + <column name="other_config" key="mac_only" type='{"type": "boolean"}'> Value used to request to assign L2 address only if neither subnet nor ipv6_prefix are specified @@ -3066,6 +3073,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 @@ -4379,6 +4391,33 @@ or </group> </table> + <table name="DHCP_Relay" title="DHCP Relay"> + <p> + OVN implements native DHCPv4 relay support which caters to the common + use case of relaying the DHCP requests to external DHCP server. + </p> + <column name="name"> + <p> + A name for the DHCP Relay. + </p> + </column> + <column name="servers"> + <p> + The DHCPv4 server IP address. + </p> + </column> + <column name="options"> + <p> + Future purpose. + </p> + </column> + <group title="Common Columns"> + <column name="external_ids"> + See <em>External IDs</em> at the beginning of this document. + </column> + </group> + </table> + <table name="Connection" title="OVSDB client connections."> <p> Configuration for a database connection to an Open vSwitch database diff --git a/tests/atlocal.in b/tests/atlocal.in index 63d891b89..32d1c374e 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -187,6 +187,9 @@ fi # Set HAVE_DHCPD find_command dhcpd +# Set HAVE_DHCLIENT +find_command dhclient + # Set HAVE_BFDD_BEACON find_command bfdd-beacon diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index dcc29ffa8..7267c7017 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -12191,6 +12191,44 @@ check_row_count nb:QoS 0 AT_CLEANUP ]) +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([check DHCP RELAY]) +ovn_start NORTHD_TYPE + +check ovn-nbctl ls-add ls0 +check ovn-nbctl lsp-add ls0 ls0-port1 +check ovn-nbctl lsp-set-addresses ls0-port1 02:00:00:00:00:10 +check ovn-nbctl lr-add lr0 +check ovn-nbctl lrp-add lr0 lrp1 02:00:00:00:00:01 192.168.1.1/24 +check ovn-nbctl lsp-add ls0 lrp1-attachment +check ovn-nbctl lsp-set-type lrp1-attachment router +check ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02 +check ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1 +check ovn-nbctl lrp-add lr0 lrp-ext 02:00:00:00:00:02 192.168.2.1/24 + +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) +check ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay +check ovn-nbctl set Logical_Switch ls0 other_config:dhcp_relay_port=lrp1-attachment + +check ovn-nbctl --wait=sb sync + +ovn-sbctl lflow-list > lflows +AT_CAPTURE_FILE([lflows]) + +AT_CHECK([grep -e "DHCP_RELAY_" lflows | sed 's/table=../table=??/'], [0], [dnl + table=??(lr_in_ip_input ), priority=110 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && ip.frag == 0 && udp.src == 68 && udp.dst == 67), action=(reg9[[7]] = dhcp_relay_req_chk(192.168.1.1, 172.16.1.1);next; /* DHCP_RELAY_REQ */) + table=??(lr_in_ip_input ), priority=110 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && ip.frag == 0 && udp.src == 67 && udp.dst == 67), action=(next;/* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_req), priority=100 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]]), action=(ip4.src=192.168.1.1;ip4.dst=172.16.1.1;udp.src=67;next; /* DHCP_RELAY_REQ */) + table=??(lr_in_dhcp_relay_req), priority=1 , match=(inport == "lrp1" && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && reg9[[7]] == 0), action=(drop; /* DHCP_RELAY_REQ */) + table=??(lr_in_dhcp_relay_resp_chk), priority=100 , match=(ip4.src == 172.16.1.1 && ip4.dst == 192.168.1.1 && udp.src == 67 && udp.dst == 67), action=(reg2 = ip4.dst;reg9[[8]] = dhcp_relay_resp_chk(192.168.1.1, 172.16.1.1);next;/* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp), priority=100 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]]), action=(ip4.src=192.168.1.1;udp.dst=68;outport="lrp1";output; /* DHCP_RELAY_RESP */) + table=??(lr_in_dhcp_relay_resp), priority=1 , match=(ip4.src == 172.16.1.1 && reg2 == 192.168.1.1 && udp.src == 67 && udp.dst == 67 && reg9[[8]] == 0), action=(drop; /* DHCP_RELAY_RESP */) + table=??(ls_in_l2_lkup ), priority=100 , match=(inport == "ls0-port1" && eth.src == 02:00:00:00:00:10 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(eth.dst=02:00:00:00:00:01;outport="lrp1-attachment";next;/* DHCP_RELAY_REQ */) +]) + +AT_CLEANUP +]) + AT_SETUP([NB_Global and SB_Global incremental processing]) ovn_start diff --git a/tests/ovn.at b/tests/ovn.at index 1ad4159cf..395bcf142 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -34273,7 +34273,7 @@ check ovn-nbctl set nb_global . options:use_common_zone="true" check ovn-nbctl --wait=hv sync # Use constants so that if tables or registers change, this test can # be updated easily. -DNAT_TABLE=15 +DNAT_TABLE=16 SNAT_TABLE=45 DNAT_ZONE_REG="NXM_NX_REG11[[0..15]]" SNAT_ZONE_REG="NXM_NX_REG12[[0..15]]" @@ -37806,3 +37806,225 @@ OVS_WAIT_FOR_OUTPUT([as hv1 ovs-ofctl dump-flows br-int table=0 |grep priority=1 OVN_CLEANUP([hv1]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([DHCP RELAY]) +ovn_start +net_add n1 + +AT_CHECK([ovn-nbctl ls-add ls0]) +AT_CHECK([ovn-nbctl lsp-add ls0 vif0]) +AT_CHECK([ovn-nbctl lsp-set-addresses vif0 "50:54:00:00:00:10"]) +AT_CHECK([ovn-nbctl lsp-add ls0 lrp1-attachment]) +AT_CHECK([ovn-nbctl lsp-set-type lrp1-attachment router]) +AT_CHECK([ovn-nbctl lsp-set-addresses lrp1-attachment 50:54:00:00:00:01]) +AT_CHECK([ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1]) + +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lrp-add lr0 lrp1 50:54:00:00:00:01 192.168.1.1/24]) +AT_CHECK([ovn-nbctl lrp-add lr0 lrp2 50:54:00:00:00:02 172.16.1.254/24]) + +AT_CHECK([ovn-nbctl ls-add ls-ext]) +AT_CHECK([ovn-nbctl lsp-add ls-ext lrp2-attachment]) +AT_CHECK([ovn-nbctl lsp-set-type lrp2-attachment router]) +AT_CHECK([ovn-nbctl lsp-set-addresses lrp2-attachment 50:54:00:00:00:02]) +AT_CHECK([ovn-nbctl lsp-set-options lrp2-attachment router-port=lrp2]) +AT_CHECK([ovn-nbctl lsp-add ls-ext ln_port]) +AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown]) +AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet]) +AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1]) + +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) +AT_CHECK([ovn-nbctl set Logical_Router_port lrp1 dhcp_relay=$dhcp_relay]) +AT_CHECK([ovn-nbctl set Logical_Switch ls0 other_config:dhcp_relay_port=lrp1-attachment]) + +sim_add hv1 +as hv1 +ovs-vsctl add-br br-phys +ovn_attach n1 br-phys 192.168.0.1 +ovs-vsctl -- add-port br-int vif0 -- \ + set interface vif0 external-ids:iface-id=vif0 \ + options:tx_pcap=hv1/vif0-tx.pcap \ + options:rxq_pcap=hv1/vif0-rx.pcap \ + ofport-request=1 +ovs-vsctl -- add-port br-phys ext0 -- \ + set interface ext0 \ + options:tx_pcap=hv1/ext0-tx.pcap \ + options:rxq_pcap=hv1/ext0-rx.pcap \ + ofport-request=2 + +ovs-vsctl set open . external_ids:ovn-bridge-mappings=physnet1:br-phys + +wait_for_ports_up +AT_CHECK([ovn-nbctl --wait=hv sync]) + +send_dhcp_packet() { + src_mac=${1} + src_ip=${2} + dst_mac=${3} + dst_ip=${4} + op_code=${5} + msg_type=${6} + yiaddr=$7 + giaddr=${8} + sid=${9} + iface=${10} + #echo "ARGS: ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10}" + echo "ARGS: $@" + if [[ $op_code == "01" ]]; then + ip_len=0111 + udp_len=00fd + src_port=0044 + else + ip_len=011d + udp_len=0109 + src_port=0043 + fi + flags=0000 + + local pkt=${dst_mac}${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip} + # udp header and dhcp header + pkt=${pkt}${src_port}0043${udp_len}0000 + pkt=${pkt}${op_code}0106006359aa760000${flags}00000000${yiaddr}00000000${giaddr}${src_mac} + # client hardware padding + pkt=${pkt}00000000000000000000 + # server hostname + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + # boot file name + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + pkt=${pkt}0000000000000000000000000000000000000000000000000000000000000000 + # dhcp magic cookie + pkt=${pkt}63825363 + # dhcp message type + pkt=${pkt}3501${msg_type} + # dhcp server identifier and subnet mask options + if [[ $op_code == "02" ]]; then + pkt=${pkt}3604${sid} + pkt=${pkt}0104ffffff00 + fi + # dhcp pad option + pkt=${pkt}00 + # dhcp end option + pkt=${pkt}ff + + tcpdump_hex "-- sending DHCP pkt on hv1-$iface" $pkt + + ovs-appctl netdev-dummy/receive $iface $pkt +} + +ovn-sbctl dump-flows > lflows +AT_CAPTURE_FILE([lflows]) + +# Get the OF table numbers +dhcp_relay_req_table=$(ovn-debug lflow-stage-to-oftable lr_in_dhcp_relay_req) +dhcp_relay_resp_table=$(ovn-debug lflow-stage-to-oftable lr_in_dhcp_relay_resp) + +# ==================================================== +# Send DHCP valid discovery +src_mac="505400000010" +src_ip=`ip_to_hex 0.0.0.0` +dst_mac="ffffffffffff" +dst_ip=`ip_to_hex 255.255.255.255` +yiaddr=`ip_to_hex 0.0.0.0` +giaddr=`ip_to_hex 0.0.0.0` +sid=$src_ip +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr $sid vif0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_req_table > pflows1_dhcp_relay_req +AT_CAPTURE_FILE([pflows1_dhcp_relay_req]) + +AT_CHECK([cat pflows1_dhcp_relay_req | grep -v NXST | grep 255.255.255.255 | grep resubmit | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=1 +]) + +# ==================================================== +# Send DHCP discovery with giaddr set +giaddr=`ip_to_hex 192.168.1.1` +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 01 01 $yiaddr $giaddr $sid vif0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_req_table > pflows2_dhcp_relay_req +AT_CAPTURE_FILE([pflows2_dhcp_relay_req]) + +AT_CHECK([cat pflows2_dhcp_relay_req | grep -v NXST | grep 255.255.255.255 | grep drop | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=1 +]) + +# ==================================================== +# Send DHCP valid offer +src_mac="50540000001f" +src_ip=`ip_to_hex 172.16.1.1` +dst_mac="505400000002" +dst_ip=`ip_to_hex 192.168.1.1` +yiaddr=`ip_to_hex 192.168.1.10` +giaddr=`ip_to_hex 192.168.1.1` +sid=$src_ip +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr $sid ext0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > pflows1_dhcp_relay_resp +AT_CAPTURE_FILE([pflows1_dhcp_relay_resp]) + +AT_CHECK([cat pflows1_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | grep resubmit | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=1 +]) +# ==================================================== +# Send DHCP offer with incorrect giaddr +giaddr=`ip_to_hex 192.168.1.10` +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr $sid ext0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > pflows2_dhcp_relay_resp +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) + +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | grep drop | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=1 +]) + +giaddr=`ip_to_hex 192.168.1.1` + +# ==================================================== +# Send DHCP offer with yiaddr outside of the subnet +yiaddr=`ip_to_hex 192.168.2.10` +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr $sid ext0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > pflows2_dhcp_relay_resp +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) + +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | grep drop | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=2 +]) + +yiaddr=`ip_to_hex 192.168.1.10` + +# ==================================================== +# Send DHCP offer with differnt server identifier +sid=`ip_to_hex 172.16.1.100` +# send packet +send_dhcp_packet $src_mac $src_ip $dst_mac $dst_ip 02 02 $yiaddr $giaddr $sid ext0 + +ovs-ofctl dump-flows br-int table=$dhcp_relay_resp_table > pflows2_dhcp_relay_resp +AT_CAPTURE_FILE([pflows2_dhcp_relay_resp]) + +AT_CHECK([cat pflows2_dhcp_relay_resp | grep -v NXST | grep 172.16.1.1 | grep drop | +cut -d ' ' -f5-5 | sed "s/,//"], [0], [dnl +n_packets=3 +]) + +sid=`ip_to_hex 172.16.1.1` + +OVN_CLEANUP([hv1 +/WARN\|DHCP_RELAY/d +]) +AT_CLEANUP +]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 5848f3901..b9f731396 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -12512,3 +12512,151 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d /connection dropped.*/d"]) AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD([ +AT_SETUP([DHCP RELAY]) +AT_SKIP_IF([test $HAVE_DHCPD = no]) +AT_SKIP_IF([test $HAVE_DHCLIENT = no]) +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) +ovn_start +OVS_TRAFFIC_VSWITCHD_START() + +ADD_BR([br-int]) +ADD_BR([br-ext]) + +ovs-ofctl add-flow br-ext action=normal +# Set external-ids in br-int needed for ovn-controller +ovs-vsctl \ + -- set Open_vSwitch . external-ids:system-id=hv1 \ + -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true + +# Start ovn-controller +start_daemon ovn-controller + +ADD_NAMESPACES(sw01) +ADD_VETH(sw01, sw01, br-int, "0", "f0:00:00:01:02:03") +ADD_NAMESPACES(sw11) +ADD_VETH(sw11, sw11, br-int, "0", "f0:00:00:02:02:03") +ADD_NAMESPACES(server) +ADD_VETH(s1, server, br-ext, "172.16.1.1/24", "f0:00:00:01:02:05", \ + "172.16.1.254") + +check ovn-nbctl lr-add R1 + +check ovn-nbctl ls-add sw0 +check ovn-nbctl ls-add sw1 +check ovn-nbctl ls-add sw-ext + +check ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24 +check ovn-nbctl lrp-add R1 rp-sw1 00:00:03:01:02:03 192.168.2.1/24 +check ovn-nbctl lrp-add R1 rp-ext 00:00:02:01:02:03 172.16.1.254/24 + +dhcp_relay=$(ovn-nbctl create DHCP_Relay servers=172.16.1.1) +check ovn-nbctl set Logical_Router_port rp-sw0 dhcp_relay=$dhcp_relay +check ovn-nbctl set Logical_Router_port rp-sw1 dhcp_relay=$dhcp_relay +check ovn-nbctl lrp-set-gateway-chassis rp-ext hv1 + +check ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \ + type=router options:router-port=rp-sw0 \ + -- lsp-set-addresses sw0-rp router +check ovn-nbctl lsp-add sw1 sw1-rp -- set Logical_Switch_Port sw1-rp \ + type=router options:router-port=rp-sw1 \ + -- lsp-set-addresses sw1-rp router + +check ovn-nbctl set Logical_Switch sw0 other_config:dhcp_relay_port=sw0-rp +check ovn-nbctl set Logical_Switch sw1 other_config:dhcp_relay_port=sw1-rp + +check ovn-nbctl lsp-add sw-ext ext-rp -- set Logical_Switch_Port ext-rp \ + type=router options:router-port=rp-ext \ + -- lsp-set-addresses ext-rp router +check ovn-nbctl lsp-add sw-ext lnet \ + -- lsp-set-addresses lnet unknown \ + -- lsp-set-type lnet localnet \ + -- lsp-set-options lnet network_name=phynet + +check ovn-nbctl lsp-add sw0 sw01 \ + -- lsp-set-addresses sw01 "f0:00:00:01:02:03" + +check ovn-nbctl lsp-add sw1 sw11 \ + -- lsp-set-addresses sw11 "f0:00:00:02:02:03" + +AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext]) + +OVN_POPULATE_ARP + +check ovn-nbctl --wait=hv sync + +DHCP_TEST_DIR="/tmp/dhcp-test" +rm -rf $DHCP_TEST_DIR +mkdir $DHCP_TEST_DIR +cat > $DHCP_TEST_DIR/dhcpd.conf <<EOF +subnet 172.16.1.0 netmask 255.255.255.0 { +} +subnet 192.168.1.0 netmask 255.255.255.0 { + range 192.168.1.10 192.168.1.10; + option routers 192.168.1.1; + option broadcast-address 192.168.1.255; + default-lease-time 60; + max-lease-time 120; +} +subnet 192.168.2.0 netmask 255.255.255.0 { + range 192.168.2.10 192.168.2.10; + option routers 192.168.2.1; + option broadcast-address 192.168.2.255; + default-lease-time 60; + max-lease-time 120; +} +EOF +cat > $DHCP_TEST_DIR/dhclien.conf <<EOF +timeout 2 +EOF + +touch $DHCP_TEST_DIR/dhcpd.leases +chown root:dhcpd $DHCP_TEST_DIR $DHCP_TEST_DIR/dhcpd.leases +chmod 775 $DHCP_TEST_DIR +chmod 664 $DHCP_TEST_DIR/dhcpd.leases + + +NETNS_DAEMONIZE([server], [dhcpd -4 -f -cf $DHCP_TEST_DIR/dhcpd.conf s1 > dhcpd.log 2>&1], [dhcpd.pid]) + +NS_CHECK_EXEC([server], [tcpdump -l -nvv -i s1 udp > pkt.pcap 2>tcpdump_err &]) +OVS_WAIT_UNTIL([grep "listening" tcpdump_err]) +on_exit 'kill $(pidof tcpdump)' + +NS_CHECK_EXEC([sw01], [dhclient -1 -q -lf $DHCP_TEST_DIR/dhclient-sw01.lease -pf $DHCP_TEST_DIR/dhclient-sw01.pid -cf $DHCP_TEST_DIR/dhclien.conf sw01]) +NS_CHECK_EXEC([sw11], [dhclient -1 -q -lf $DHCP_TEST_DIR/dhclient-sw11.lease -pf $DHCP_TEST_DIR/dhclient-sw11.pid -cf $DHCP_TEST_DIR/dhclien.conf sw11]) + +OVS_WAIT_UNTIL([ + total_pkts=$(cat pkt.pcap | wc -l) + test ${total_pkts} -ge 8 +]) + +on_exit 'kill `cat $DHCP_TEST_DIR/dhclient-sw01.pid` && +kill `cat $DHCP_TEST_DIR/dhclient-sw11.pid` && rm -rf $DHCP_TEST_DIR' + +NS_CHECK_EXEC([sw01], [ip addr show sw01 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl +192.168.1.10 +]) +NS_CHECK_EXEC([sw11], [ip addr show sw11 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'], [0], [dnl +192.168.2.10 +]) +OVS_APP_EXIT_AND_WAIT([ovn-controller]) + +as ovn-sb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as ovn-nb +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) + +as northd +OVS_APP_EXIT_AND_WAIT([ovn-northd]) + +as +OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d +/failed to query port patch-.*/d +/.*terminating with signal 15.*/d"]) +AT_CLEANUP +])