diff --git a/northd/northd.c b/northd/northd.c index b01e40ecda..8fc49a63a2 100644 --- a/northd/northd.c +++ b/northd/northd.c @@ -6065,6 +6065,27 @@ skip_port_from_conntrack(const struct ovn_datapath *od, struct ovn_port *op, free(egress_match); } +static void +build_stateless_for_lb_filter(const struct ovn_datapath *od, + const struct nbrec_acl *acl, + struct lflow_table *lflows, + struct lflow_ref *lflow_ref) +{ + const char *action = REGBIT_ACL_VERDICT_ALLOW" = 1;" + REGBIT_CONNTRACK_COMMIT" = 1; next;"; + if (!strcmp(acl->direction, "from-lport")) { + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_PRE_ACL, 65532, + acl->match, "next;", &acl->header_, lflow_ref); + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_IN_ACL_EVAL, + 65532, acl->match, action, &acl->header_, lflow_ref); + } else { + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_PRE_ACL, 65532, + acl->match, "next;", &acl->header_, lflow_ref); + ovn_lflow_add_with_hint(lflows, od, S_SWITCH_OUT_ACL_EVAL, + 65532, acl->match, action, &acl->header_, lflow_ref); + } +} + static void build_stateless_filter(const struct ovn_datapath *od, const struct nbrec_acl *acl, @@ -6095,10 +6116,17 @@ build_stateless_filters(const struct ovn_datapath *od, struct lflow_table *lflows, struct lflow_ref *lflow_ref) { + bool skip_stateless = smap_get_bool(&od->nbs->other_config, + "skip_stateless", false); + for (size_t i = 0; i < od->nbs->n_acls; i++) { const struct nbrec_acl *acl = od->nbs->acls[i]; if (!strcmp(acl->action, "allow-stateless")) { - build_stateless_filter(od, acl, lflows, lflow_ref); + if (skip_stateless) { + build_stateless_for_lb_filter(od, acl, lflows, lflow_ref); + } else { + build_stateless_filter(od, acl, lflows, lflow_ref); + } } } @@ -6114,7 +6142,11 @@ build_stateless_filters(const struct ovn_datapath *od, const struct nbrec_acl *acl = ls_pg_rec->nb_pg->acls[i]; if (!strcmp(acl->action, "allow-stateless")) { - build_stateless_filter(od, acl, lflows, lflow_ref); + if (skip_stateless) { + build_stateless_for_lb_filter(od, acl, lflows, lflow_ref); + } else { + build_stateless_filter(od, acl, lflows, lflow_ref); + } } } } diff --git a/ovn-nb.xml b/ovn-nb.xml index 4b86f432db..b093219c26 100644 --- a/ovn-nb.xml +++ b/ovn-nb.xml @@ -795,6 +795,13 @@ to 0, which means disabled. + + For the correct operation of the load balancer, when using stateless + ACLs and for the load balancer on the logical switch that matches these + rules, enable this option. Without this option, packets governed + by stateless ACLs will be processed as stateless and LB will not work. + + diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at index 21d9d63ab8..38524b06ae 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -14384,3 +14384,74 @@ AT_CHECK([ovn-sbctl lflow-list S1 | grep ls_out_acl_action | grep priority=500 | AT_CLEANUP ]) + +OVN_FOR_EACH_NORTHD_NO_HV([ +AT_SETUP([ACL with skip_stateless option]) +ovn_start + +check ovn-nbctl ls-add ls1 + +check ovn-nbctl --wait=sb acl-add ls1 to-lport 100 1 allow-stateless +check ovn-nbctl --wait=sb acl-add ls1 from-lport 150 1 allow-stateless +ovn-sbctl dump-flows ls1 > sw0flows + +AT_CHECK([grep -e "ls_in_pre_acl" -e "ls_out_pre_acl" sw0flows | ovn_strip_lflows], [0], [dnl + table=??(ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_in_pre_acl ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) + table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) +]) + +check ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80 +check ovn-nbctl --wait=sb ls-lb-add ls1 lb1 + +ovn-sbctl dump-flows ls1 > ls1flows +AT_CHECK([grep -e "ls_in_pre_acl" -e "ls_out_pre_acl" ls1flows | ovn_strip_lflows], [0], [dnl + table=??(ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_in_pre_acl ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) + table=??(ls_in_pre_acl ), priority=1150 , match=(1), action=(reg0[[16]] = 1; next;) + table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) + table=??(ls_out_pre_acl ), priority=1100 , match=(1), action=(reg0[[16]] = 1; next;) +]) + +AT_CHECK([grep "ls_out_acl_eval" ls1flows | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=0 , match=(1), action=(next;) + table=??(ls_out_acl_eval ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) + table=??(ls_out_acl_eval ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=1100 , match=((1)), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=34000, match=(eth.src == $svc_monitor_mac), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(reg8[[16]] = 1; ct_commit_nat;) + table=??(ls_out_acl_eval ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(reg8[[17]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(reg8[[16]] = 1; next;) +]) + +ovn-nbctl set logical-switch ls1 other_config:skip_stateless=true + +ovn-sbctl dump-flows ls1 > ls1flows + +AT_CHECK([grep -e "ls_in_pre_acl" -e "ls_out_pre_acl" ls1flows | ovn_strip_lflows], [0], [dnl + table=??(ls_in_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_in_pre_acl ), priority=110 , match=(eth.dst == $svc_monitor_mac), action=(next;) + table=??(ls_in_pre_acl ), priority=65532, match=(1), action=(next;) + table=??(ls_out_pre_acl ), priority=0 , match=(1), action=(next;) + table=??(ls_out_pre_acl ), priority=110 , match=(eth.src == $svc_monitor_mac), action=(next;) + table=??(ls_out_pre_acl ), priority=65532, match=(1), action=(next;) +]) + +AT_CHECK([grep "ls_out_acl_eval" ls1flows | ovn_strip_lflows], [0], [dnl + table=??(ls_out_acl_eval ), priority=0 , match=(1), action=(next;) + table=??(ls_out_acl_eval ), priority=1 , match=(ip && !ct.est), action=(reg0[[1]] = 1; next;) + table=??(ls_out_acl_eval ), priority=1 , match=(ip && ct.est && ct_mark.blocked == 1), action=(reg0[[1]] = 1; reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=1100 , match=((1)), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=34000, match=(eth.src == $svc_monitor_mac), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(!ct.est && ct.rel && !ct.new && !ct.inv && ct_mark.blocked == 0), action=(reg8[[16]] = 1; ct_commit_nat;) + table=??(ls_out_acl_eval ), priority=65532, match=(1), action=(reg8[[16]] = 1;reg0[[1]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(ct.est && !ct.rel && !ct.new && !ct.inv && ct.rpl && ct_mark.blocked == 0), action=(reg8[[16]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(ct.inv || (ct.est && ct.rpl && ct_mark.blocked == 1)), action=(reg8[[17]] = 1; next;) + table=??(ls_out_acl_eval ), priority=65532, match=(nd || nd_ra || nd_rs || mldv1 || mldv2), action=(reg8[[16]] = 1; next;) +]) + +AT_CLEANUP +]) diff --git a/tests/system-ovn.at b/tests/system-ovn.at index 19ec1eb8df..ac0d287648 100644 --- a/tests/system-ovn.at +++ b/tests/system-ovn.at @@ -9839,6 +9839,42 @@ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +#add skip_stateless options on ls +check ovn-nbctl set logical_switch foo other_config skip_stateless=true +check ovn-nbctl set logical_switch bar other_config skip_stateless=true + +# Wait for ovn-controller to catch up. +ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ +grep 'nat(dst=192.168.2.2:80)']) +zone_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep foo1 | cut -d ' ' -f2) + +OVS_START_L7([bar1], [http]) + +AT_CHECK([ip netns exec foo1 wget 192.168.2.2 -t 3 -T 1], [0], [ignore], [ignore]) + +# check conntrack zone has tcp entry +AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=$zone_id | \ +FORMAT_CT(192.168.1.2) | \ +sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +tcp,orig=(src=192.168.1.2,dst=192.168.2.2,sport=,dport=),reply=(src=192.168.2.2,dst=192.168.1.2,sport=,dport=),zone=,protoinfo=(state=) +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) + +# now check with VIP +AT_CHECK([ip netns exec foo1 wget 30.30.30.30 -t 3 -T 1], [0], [ignore], [ignore]) + +# check conntrack zone has tcp entry +AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=$zone_id | \ +FORMAT_CT(30.30.30.30) | \ +sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +tcp,orig=(src=192.168.1.2,dst=30.30.30.30,sport=,dport=),reply=(src=192.168.2.2,dst=192.168.1.2,sport=,dport=),zone=,mark=2,protoinfo=(state=) +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) + OVS_APP_EXIT_AND_WAIT([ovn-controller]) as ovn-sb @@ -9983,6 +10019,42 @@ sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl AT_CHECK([ovs-appctl dpctl/flush-conntrack]) +check ovn-nbctl set logical_switch foo other_config skip_stateless=true +check ovn-nbctl set logical_switch bar other_config skip_stateless=true + +# Wait for ovn-controller to catch up. +ovn-nbctl --wait=hv sync + +OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \ +grep 'nat(dst=\[[fd12::2\]]:80)']) + +zone_id=$(ovn-appctl -t ovn-controller ct-zone-list | grep foo1 | cut -d ' ' -f2) + +OVS_START_L7([bar1], [http6]) +AT_CHECK([ip netns exec foo1 wget http://[[fd12::2]] -t 3 -T 1], [0], [ignore], [ignore]) + +# check conntrack zone has tcp entry +AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=$zone_id | \ +FORMAT_CT(fd12::2) | grep -v fe80 | \ +sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +tcp,orig=(src=fd11::2,dst=fd12::2,sport=,dport=),reply=(src=fd12::2,dst=fd11::2,sport=,dport=),zone=,protoinfo=(state=) +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) + +# now check with VIP +AT_CHECK([ip netns exec foo1 wget http://[[fd30::2]] -t 3 -T 1], [0], [ignore], [ignore]) + +# check conntrack zone has tcp entry +AT_CHECK([ovs-appctl dpctl/dump-conntrack zone=$zone_id | \ +FORMAT_CT(fd30::2) | grep -v fe80 | \ +sed -e 's/zone=[[0-9]]*/zone=/'], [0], [dnl +tcp,orig=(src=fd11::2,dst=fd30::2,sport=,dport=),reply=(src=fd12::2,dst=fd11::2,sport=,dport=),zone=,mark=2,protoinfo=(state=) +]) + +AT_CHECK([ovs-appctl dpctl/flush-conntrack]) + +# OVS_APP_EXIT_AND_WAIT([ovn-controller]) as ovn-sb