Skip to content

Commit

Permalink
northd: Options for pass connection tracker for stateless lflow with LB
Browse files Browse the repository at this point in the history
Currently, packets that match stateless ACL bypass conntrack,
even with a load balancer (LB) in the switch. When the skip_stateless
option is set on the logical switch, a commit will occur for the
proper functioning of the load balancers together with overlapping
stateless ACL.

Signed-off-by: Alexandra Rukomoinikova <arukomoinikova@k2.cloud>
Tested-by: Evgeniy Kovalev <EvgKovalev@k2.cloud>
Signed-off-by: 0-day Robot <robot@bytheb.org>
  • Loading branch information
Alexandra Rukomoinikova authored and ovsrobot committed Dec 28, 2024
1 parent 072b8ec commit d51f5c0
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 2 deletions.
36 changes: 34 additions & 2 deletions northd/northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
}

Expand All @@ -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);
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions ovn-nb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,13 @@
to 0, which means disabled.
</column>

<column name="other_config" key="skip_stateless" type='{"type": "boolean"}'>
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.
</column>

<column name="other_config" key="ct-zone-limit"
type='{"type": "integer", "minInteger": 0,
"maxInteger": 4294967295}'>
Expand Down
71 changes: 71 additions & 0 deletions tests/ovn-northd.at
Original file line number Diff line number Diff line change
Expand Up @@ -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
])
72 changes: 72 additions & 0 deletions tests/system-ovn.at
Original file line number Diff line number Diff line change
Expand Up @@ -9839,6 +9839,42 @@ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [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=<cleared>/'], [0], [dnl
tcp,orig=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
])

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=<cleared>/'], [0], [dnl
tcp,orig=(src=192.168.1.2,dst=30.30.30.30,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
])

AT_CHECK([ovs-appctl dpctl/flush-conntrack])

OVS_APP_EXIT_AND_WAIT([ovn-controller])

as ovn-sb
Expand Down Expand Up @@ -9983,6 +10019,42 @@ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [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=<cleared>/'], [0], [dnl
tcp,orig=(src=fd11::2,dst=fd12::2,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd11::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
])

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=<cleared>/'], [0], [dnl
tcp,orig=(src=fd11::2,dst=fd30::2,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd11::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
])

AT_CHECK([ovs-appctl dpctl/flush-conntrack])

#
OVS_APP_EXIT_AND_WAIT([ovn-controller])

as ovn-sb
Expand Down

0 comments on commit d51f5c0

Please sign in to comment.