@@ -556,9 +556,14 @@ ofpact_nest_get_action_len(const struct ofpact_nest *on)
/* Bits for 'flags' in struct nx_action_conntrack.
*
* If NX_CT_F_COMMIT is set, then the connection entry is moved from the
- * unconfirmed to confirmed list in the tracker. */
+ * unconfirmed to confirmed list in the tracker.
+ * If NX_CT_F_FORCE is set, in addition to NX_CT_F_COMMIT, then the conntrack
+ * entry is replaced with a new one in case the original direction of the
+ * existing entry is opposite of the current packet direction.
+ */
enum nx_conntrack_flags {
NX_CT_F_COMMIT = 1 << 0,
+ NX_CT_F_FORCE = 1 << 1,
};
/* Magic value for struct nx_action_conntrack 'recirc_table' field, to specify
@@ -239,12 +239,21 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
static struct conn *
process_one(struct conntrack *ct, struct dp_packet *pkt,
struct conn_lookup_ctx *ctx, uint16_t zone,
- bool commit, long long now)
+ bool force, bool commit, long long now)
{
unsigned bucket = hash_to_bucket(ctx->hash);
struct conn *conn = ctx->conn;
uint16_t state = 0;
+ /* Delete found entry if in wrong direction. 'force' implies commit. */
+ if (conn && force && ctx->reply) {
+ ovs_list_remove(&conn->exp_node);
+ hmap_remove(&ct->buckets[bucket].connections, &conn->node);
+ atomic_count_dec(&ct->n_conn);
+ delete_conn(conn);
+ conn = NULL;
+ }
+
if (conn) {
if (ctx->related) {
state |= CS_RELATED;
@@ -301,7 +310,7 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
* 'setlabel' behaves similarly for the connection label.*/
int
conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
- ovs_be16 dl_type, bool commit, uint16_t zone,
+ ovs_be16 dl_type, bool force, bool commit, uint16_t zone,
const uint32_t *setmark,
const struct ovs_key_ct_labels *setlabel,
const char *helper)
@@ -364,7 +373,8 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
conn_key_lookup(ctb, &ctxs[j], now);
- conn = process_one(ct, pkts[j], &ctxs[j], zone, commit, now);
+ conn = process_one(ct, pkts[j], &ctxs[j], zone, force, commit,
+ now);
if (conn && setmark) {
set_mark(pkts[j], conn, setmark[0], setmark[1]);
@@ -65,7 +65,7 @@ void conntrack_init(struct conntrack *);
void conntrack_destroy(struct conntrack *);
int conntrack_execute(struct conntrack *, struct dp_packet_batch *,
- ovs_be16 dl_type, bool commit,
+ ovs_be16 dl_type, bool force, bool commit,
uint16_t zone, const uint32_t *setmark,
const struct ovs_key_ct_labels *setlabel,
const char *helper);
@@ -4734,6 +4734,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_CT: {
const struct nlattr *b;
+ bool force = false;
bool commit = false;
unsigned int left;
uint16_t zone = 0;
@@ -4746,6 +4747,9 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
enum ovs_ct_attr sub_type = nl_attr_type(b);
switch(sub_type) {
+ case OVS_CT_ATTR_FORCE_COMMIT:
+ force = true;
+ /* fall through. */
case OVS_CT_ATTR_COMMIT:
commit = true;
break;
@@ -4768,8 +4772,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
}
}
- conntrack_execute(&dp->conntrack, packets_, aux->flow->dl_type, commit,
- zone, setmark, setlabel, helper);
+ conntrack_execute(&dp->conntrack, packets_, aux->flow->dl_type, force,
+ commit, zone, setmark, setlabel, helper);
break;
}
@@ -720,6 +720,7 @@ format_odp_ct_nat(struct ds *ds, const struct nlattr *attr)
static const struct nl_policy ovs_conntrack_policy[] = {
[OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .optional = true, },
+ [OVS_CT_ATTR_FORCE_COMMIT] = { .type = NL_A_FLAG, .optional = true, },
[OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true, },
[OVS_CT_ATTR_MARK] = { .type = NL_A_UNSPEC, .optional = true,
.min_len = sizeof(uint32_t) * 2 },
@@ -738,7 +739,7 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
const uint32_t *mark;
const char *helper;
uint16_t zone;
- bool commit;
+ bool commit, force;
const struct nlattr *nat;
if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
@@ -747,6 +748,7 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
}
commit = a[OVS_CT_ATTR_COMMIT] ? true : false;
+ force = a[OVS_CT_ATTR_FORCE_COMMIT] ? true : false;
zone = a[OVS_CT_ATTR_ZONE] ? nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]) : 0;
mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL;
label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL;
@@ -754,11 +756,14 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
nat = a[OVS_CT_ATTR_NAT];
ds_put_format(ds, "ct");
- if (commit || zone || mark || label || helper || nat) {
+ if (commit || force || zone || mark || label || helper || nat) {
ds_put_cstr(ds, "(");
if (commit) {
ds_put_format(ds, "commit,");
}
+ if (force) {
+ ds_put_format(ds, "force_commit,");
+ }
if (zone) {
ds_put_format(ds, "zone=%"PRIu16",", zone);
}
@@ -1452,6 +1457,7 @@ parse_conntrack_action(const char *s_, struct ofpbuf *actions)
const char *helper = NULL;
size_t helper_len = 0;
bool commit = false;
+ bool force_commit = false;
uint16_t zone = 0;
struct {
uint32_t value;
@@ -1486,6 +1492,11 @@ find_end:
s += n;
continue;
}
+ if (ovs_scan(s, "force_commit%n", &n)) {
+ force_commit = true;
+ s += n;
+ continue;
+ }
if (ovs_scan(s, "zone=%"SCNu16"%n", &zone, &n)) {
s += n;
continue;
@@ -1536,10 +1547,15 @@ find_end:
}
s++;
}
+ if (commit && force_commit) {
+ return -EINVAL;
+ }
start = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CT);
if (commit) {
nl_msg_put_flag(actions, OVS_CT_ATTR_COMMIT);
+ } else if (force_commit) {
+ nl_msg_put_flag(actions, OVS_CT_ATTR_FORCE_COMMIT);
}
if (zone) {
nl_msg_put_u16(actions, OVS_CT_ATTR_ZONE, zone);
@@ -5265,6 +5265,15 @@ format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
* tracker, and the state of the connection will be stored beyond the
* lifetime of packet processing.
*
+ * A committed connection always has the directionality of the packet that
+ * caused the connection to be committed in the first place. This is the
+ * "original direction" of the connection, and the opposite direction is
+ * the "reply direction". If a connection is already committed, but it is
+ * then decided that the original direction should be the opposite of the
+ * existing connection, NX_CT_F_FORCE flag may be used in addition to
+ * NX_CT_F_COMMIT flag to in effect terminate the existing connection and
+ * start a new one in the current direction.
+ *
* Connections may transition back into the uncommitted state due to
* external timers, or due to the contents of packets that are sent to the
* connection tracker. This behaviour is outside of the scope of the
@@ -5326,7 +5335,7 @@ format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
*
* Zero or more actions may immediately follow this action. These actions will
* be executed within the context of the connection tracker, and they require
- * the NX_CT_F_COMMIT flag to be set.
+ * NX_CT_F_COMMIT flag be set.
*/
struct nx_action_conntrack {
ovs_be16 type; /* OFPAT_VENDOR. */
@@ -5391,9 +5400,16 @@ decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
{
const size_t ct_offset = ofpacts_pull(out);
struct ofpact_conntrack *conntrack = ofpact_put_CT(out);
+ int error;
+
conntrack->flags = ntohs(nac->flags);
+ if (conntrack->flags & NX_CT_F_FORCE &&
+ !(conntrack->flags & NX_CT_F_COMMIT)) {
+ error = OFPERR_OFPBAC_BAD_ARGUMENT;
+ goto out;
+ }
- int error = decode_ct_zone(nac, conntrack, vl_mff_map);
+ error = decode_ct_zone(nac, conntrack, vl_mff_map);
if (error) {
goto out;
}
@@ -5489,6 +5505,8 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
while (ofputil_parse_key_value(&arg, &key, &value)) {
if (!strcmp(key, "commit")) {
oc->flags |= NX_CT_F_COMMIT;
+ } else if (!strcmp(key, "force")) {
+ oc->flags |= NX_CT_F_FORCE;
} else if (!strcmp(key, "table")) {
error = str_to_u8(value, "recirc_table", &oc->recirc_table);
if (!error && oc->recirc_table == NX_CT_RECIRC_NONE) {
@@ -5534,7 +5552,9 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
break;
}
}
-
+ if (oc->flags & NX_CT_F_FORCE && !(oc->flags & NX_CT_F_COMMIT)) {
+ error = xasprintf("\"force\" flag requires \"commit\" flag.");
+ }
ofpact_finish_CT(ofpacts, &oc);
ofpbuf_push_uninit(ofpacts, ct_offset);
return error;
@@ -5568,6 +5588,9 @@ format_CT(const struct ofpact_conntrack *a, struct ds *s)
if (a->flags & NX_CT_F_COMMIT) {
ds_put_format(s, "%scommit%s,", colors.value, colors.end);
}
+ if (a->flags & NX_CT_F_FORCE) {
+ ds_put_format(s, "%sforce%s,", colors.value, colors.end);
+ }
if (a->recirc_table != NX_CT_RECIRC_NONE) {
ds_put_format(s, "%stable=%s%"PRIu8",",
colors.special, colors.end, a->recirc_table);
@@ -4972,7 +4972,8 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT);
if (ofc->flags & NX_CT_F_COMMIT) {
- nl_msg_put_flag(ctx->odp_actions, OVS_CT_ATTR_COMMIT);
+ nl_msg_put_flag(ctx->odp_actions, ofc->flags & NX_CT_F_FORCE ?
+ OVS_CT_ATTR_FORCE_COMMIT : OVS_CT_ATTR_COMMIT);
}
nl_msg_put_u16(ctx->odp_actions, OVS_CT_ATTR_ZONE, zone);
put_ct_mark(&ctx->xin->flow, ctx->odp_actions, ctx->wc);
@@ -332,6 +332,22 @@ ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
ct(commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random))
ct(commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255))
+ct(force_commit)
+ct(force_commit,zone=5)
+ct(force_commit,mark=0xa0a0a0a0/0xfefefefe)
+ct(force_commit,label=0x1234567890abcdef1234567890abcdef/0xf1f2f3f4f5f6f7f8f9f0fafbfcfdfeff)
+ct(force_commit,helper=ftp)
+ct(nat)
+ct(force_commit,nat(src))
+ct(force_commit,nat(dst))
+ct(force_commit,nat(src=10.0.0.240,random))
+ct(force_commit,nat(src=10.0.0.240:32768-65535,random))
+ct(force_commit,nat(dst=10.0.0.128-10.0.0.254,hash))
+ct(force_commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))
+ct(force_commit,nat(src=fe80::20c:29ff:fe88:a18b,random))
+ct(force_commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))
+ct(force_commit,nat(src=[[fe80::20c:29ff:fe88:1]]-[[fe80::20c:29ff:fe88:a18b]]:255-4096,random))
+ct(force_commit,helper=ftp,nat(src=10.1.1.240-10.1.1.255))
trunc(100)
clone(1)
clone(clone(push_vlan(vid=12,pcp=0),2),1)
@@ -161,6 +161,12 @@ ffff 0018 00002320 0023 0000 00000000 0000 FF 000000 0000
# actions=ct(commit)
ffff 0018 00002320 0023 0001 00000000 0000 FF 000000 0000
+# actions=ct(commit,force)
+ffff 0018 00002320 0023 0003 00000000 0000 FF 000000 0000
+
+# bad OpenFlow10 actions: OFPBAC_BAD_ARGUMENT
+ffff 0018 00002320 0023 0002 00000000 0000 FF 000000 0000
+
# actions=ct(table=10)
ffff 0018 00002320 0023 0000 00000000 0000 0A 000000 0000
@@ -182,6 +188,10 @@ ffff 0018 00002320 0023 0000 00010004 001F FF 000000 0000
ffff 0030 00002320 0023 0001 00000000 0000 FF 000000 0000 dnl
ffff 0018 00002320 0007 001f 0001d604 000000000000f009
+# actions=ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[]))
+ffff 0030 00002320 0023 0003 00000000 0000 FF 000000 0000 dnl
+ffff 0018 00002320 0007 001f 0001d604 000000000000f009
+
# bad OpenFlow10 actions: OFPBAC_BAD_SET_ARGUMENT
& ofp_actions|WARN|cannot set CT fields outside of ct action
ffff 0018 00002320 0007 001f 0001d604 000000000000f009
@@ -8528,6 +8528,88 @@ udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - conntrack - force commit])
+OVS_VSWITCHD_START
+
+add_of_ports br0 1 2
+
+AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg vconn:info ofproto_dpif:info])
+
+dnl Allow new connections on p1->p2, but not on p2->p1.
+AT_DATA([flows.txt], [dnl
+dnl Table 0
+dnl
+table=0,priority=100,arp,action=normal
+table=0,priority=10,in_port=1,udp,action=ct(commit),controller
+table=0,priority=10,in_port=2,udp,action=ct(table=1)
+table=0,priority=1,action=drop
+dnl
+dnl Table 1
+dnl
+table=1,priority=10,in_port=2,ct_state=+est,udp,action=ct(force,commit),controller
+table=1,priority=1,action=drop
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)'])
+
+dnl OK, now start a new connection from port 1.
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)'])
+
+dnl Now try a reply from port 2.
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)'])
+
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4])
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+
+dnl Check this output. We only see the latter two packets, not the first.
+dnl Note that the first packet doesn't have the ct_state bits set. This
+dnl happens because the ct_state field is available only after recirc.
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=1,tp_dst=2 udp_csum:e9d6
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=1,ct_tp_dst=2,in_port=2 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=2,tp_dst=1 udp_csum:e9d6
+])
+
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl -P nxt_packet_in --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+
+dnl OK, now start a second connection from port 1
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=3,dst=4)'])
+
+dnl Now try a reply from port 2.
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=4,dst=3)'])
+
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 4])
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+
+dnl Check this output. We should see both packets
+dnl Note that the first packet doesn't have the ct_state bits set. This
+dnl happens because the ct_state field is available only after recirc.
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=3,tp_dst=4 udp_csum:e9d2
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=est|rpl|trk,ct_nw_src=10.1.1.1,ct_nw_dst=10.1.1.2,ct_nw_proto=17,ct_tp_src=3,ct_tp_dst=4,in_port=2 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:0a,dl_dst=50:54:00:00:00:09,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4,tp_dst=3 udp_csum:e9d2
+])
+
+dnl
+dnl Check that the directionality has been changed by force commit.
+dnl
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | sort], [], [dnl
+udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2)
+udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=4,dport=3),reply=(src=10.1.1.1,dst=10.1.1.2,sport=3,dport=4)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - conntrack - ipv6])
OVS_VSWITCHD_START
@@ -9033,6 +9115,9 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.1.1.1,dst=10.1.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=1,dst=2)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:0a,dst=50:54:00:00:00:09),eth_type(0x0800),ipv4(src=10.1.1.2,dst=10.1.1.1,proto=17,tos=0,ttl=64,frag=no),udp(src=2,dst=1)'])
+# Give time for logs to appear.
+ovs-appctl revalidator/wait
+
AT_CHECK([cat ovs-vswitchd.log | strip_ufid | filter_flow_install], [0], [dnl
ct_state(+rpl+trk),ct_label(0x1),recirc_id(0x1),in_port(2),eth_type(0x0800),ipv4(frag=no), actions:1
recirc_id(0),in_port(1),eth_type(0x0800),ipv4(proto=17,frag=no),udp(src=1), actions:ct(commit,label=0x1),2
@@ -653,6 +653,59 @@ udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([conntrack - force commit])
+CHECK_CONNTRACK()
+OVS_TRAFFIC_VSWITCHD_START()
+AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg ofproto_dpif_upcall:dbg])
+
+ADD_NAMESPACES(at_ns0, at_ns1)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+
+AT_DATA([flows.txt], [dnl
+priority=1,action=drop
+priority=10,arp,action=normal
+priority=100,in_port=1,udp,action=ct(commit),controller
+priority=100,in_port=2,ct_state=-trk,udp,action=ct(table=0)
+priority=100,in_port=2,ct_state=+trk+est,udp,action=ct(force,commit,table=1)
+table=1,in_port=2,ct_state=+trk,udp,action=controller
+])
+
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_CHECK([ovs-ofctl monitor br0 65534 invalid_ttl --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+
+dnl Send an unsolicited reply from port 2. This should be dropped.
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"])
+
+dnl OK, now start a new connection from port 1.
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
+
+dnl Now try a reply from port 2.
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=2 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101020a0101010002000100080000 actions=resubmit(,0)"])
+
+AT_CHECK([ovs-appctl revalidator/purge], [0])
+
+dnl Check this output. We only see the latter two packets, not the first.
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN2 (xid=0x0): cookie=0x0 total_len=42 in_port=1 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1,tp_dst=2 udp_csum:0
+NXT_PACKET_IN2 (xid=0x0): table_id=1 cookie=0x0 total_len=42 ct_state=new|trk,ct_nw_src=10.1.1.2,ct_nw_dst=10.1.1.1,ct_nw_proto=17,ct_tp_src=2,ct_tp_dst=1,in_port=2 (via action) data_len=42 (unbuffered)
+udp,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=2,tp_dst=1 udp_csum:0
+])
+
+dnl
+dnl Check that the directionality has been changed by force commit.
+dnl
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep "orig=.src=10\.1\.1\.2,"], [], [dnl
+udp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=2,dport=1),reply=(src=10.1.1.1,dst=10.1.1.2,sport=1,dport=2)
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([conntrack - IPv4 ping])
CHECK_CONNTRACK()
OVS_TRAFFIC_VSWITCHD_START()
@@ -88,7 +88,8 @@ ct_thread_main(void *aux_)
pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
ovs_barrier_block(&barrier);
for (i = 0; i < n_pkts; i += batch_size) {
- conntrack_execute(&ct, pkt_batch, dl_type, true, 0, NULL, NULL, NULL);
+ conntrack_execute(&ct, pkt_batch, dl_type, false, true, 0, NULL, NULL,
+ NULL);
}
ovs_barrier_block(&barrier);
destroy_packets(pkt_batch);
@@ -170,15 +171,15 @@ pcap_batch_execute_conntrack(struct conntrack *ct,
}
if (flow.dl_type != dl_type) {
- conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL,
- NULL);
+ conntrack_execute(ct, &new_batch, dl_type, false, true, 0, NULL,
+ NULL, NULL);
dp_packet_batch_init(&new_batch);
}
new_batch.packets[new_batch.count++] = packet;;
}
if (!dp_packet_batch_is_empty(&new_batch)) {
- conntrack_execute(ct, &new_batch, dl_type, true, 0, NULL, NULL, NULL);
+ conntrack_execute(ct, &new_batch, dl_type, false, true, 0, NULL, NULL, NULL);
}
}
@@ -1037,6 +1037,18 @@ Commit the connection to the connection tracking module. Information about the
connection will be stored beyond the lifetime of the packet in the pipeline.
Some \fBct_state\fR flags are only available for committed connections.
.RE
+.IP \fBforce\fR
+.RS
+A committed connection always has the directionality of the packet
+that caused the connection to be committed in the first place. This
+is the ``original direction'' of the connection, and the opposite
+direction is the ``reply direction''. If a connection is already
+committed, but it is in the wrong direction, \fBforce\fR flag may be
+used in addition to \fBcommit\fR flag to effectively terminate the
+existing connection and start a new one in the current direction.
+This flag has no effect if the original direction of the connection is
+already the same as that of the current packet.
+.RE
.IP \fBtable=\fInumber\fR
Fork pipeline processing in two. The original instance of the packet will
continue processing the current actions list as an untracked packet. An
Userspace support for force commit. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> --- include/openvswitch/ofp-actions.h | 7 +++- lib/conntrack.c | 16 ++++++-- lib/conntrack.h | 2 +- lib/dpif-netdev.c | 8 +++- lib/odp-util.c | 20 ++++++++- lib/ofp-actions.c | 29 +++++++++++-- ofproto/ofproto-dpif-xlate.c | 3 +- tests/odp.at | 16 ++++++++ tests/ofp-actions.at | 10 +++++ tests/ofproto-dpif.at | 85 +++++++++++++++++++++++++++++++++++++++ tests/system-traffic.at | 53 ++++++++++++++++++++++++ tests/test-conntrack.c | 9 +++-- utilities/ovs-ofctl.8.in | 12 ++++++ 13 files changed, 253 insertions(+), 17 deletions(-)