[ovs-dev] [PATCH 6/9] netlink-conntrack: Add support for querying conntrack exp

Yi-Hung Wei yihung.wei at gmail.com
Fri Aug 25 22:51:16 UTC 2017


The ct_state of an uncommmited new flow is marked as related if the flow
is in the conntrack expectation table. In order for ofproto/trace to
identify the ct_state of a new related flow, this patch utilizes
NFNL_SUBSYS_CTNETLINK_EXP netlink subsystem to query the conntrack
expectation table, therefore, we can mark the ct_state of a related flow
correctly.

Signed-off-by: Yi-Hung Wei <yihung.wei at gmail.com>
---
 lib/ct-dpif.h           |  13 ++++
 lib/netlink-conntrack.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/netlink-conntrack.h |   2 +
 tests/atlocal.in        |   3 +
 tests/system-traffic.at | 137 ++++++++++++++++++++++++++++++++
 5 files changed, 358 insertions(+)

diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index f4ca07b5e776..9122e8cd752b 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -177,6 +177,19 @@ struct ct_dpif_info {
     uint32_t ct_state;
 };
 
+struct ct_dpif_exp_entry {
+    struct ct_dpif_tuple tuple;
+    struct ct_dpif_tuple tuple_mask;
+    struct ct_dpif_tuple tuple_master;
+    struct ct_dpif_tuple tuple_nat;
+    struct ct_dpif_helper helper;
+    uint32_t timeout;
+    uint32_t id;
+    uint32_t nat_dir;
+    uint32_t exp_flags;
+    uint32_t exp_class;
+};
+
 enum {
     CT_STATS_UDP,
     CT_STATS_TCP,
diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c
index 93cd0ac2c34f..de1e467dc61a 100644
--- a/lib/netlink-conntrack.c
+++ b/lib/netlink-conntrack.c
@@ -70,6 +70,15 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 #define CTA_TIMESTAMP_START 1
 #define CTA_TIMESTAMP_STOP  2
 
+#define CTA_EXPECT_ZONE   (CTA_EXPECT_HELP_NAME + 1)
+#define CTA_EXPECT_FLAGS  (CTA_EXPECT_HELP_NAME + 2)
+#define CTA_EXPECT_CLASS  (CTA_EXPECT_HELP_NAME + 3)
+#define CTA_EXPECT_NAT    (CTA_EXPECT_HELP_NAME + 4)
+#define CTA_EXPECT_FN     (CTA_EXPECT_HELP_NAME + 5)
+
+#define CTA_EXPECT_NAT_DIR      1
+#define CTA_EXPECT_NAT_TUPLE    2
+
 #define IPS_TEMPLATE_BIT 11
 #define IPS_TEMPLATE (1 << IPS_TEMPLATE_BIT)
 
@@ -99,6 +108,20 @@ static const struct nl_policy nfnlgrp_conntrack_policy[] = {
      * CTA_LABELS_MASK are not received from kernel. */
 };
 
+static const struct nl_policy nfnlgrp_conntrack_exp_policy[] = {
+    [CTA_EXPECT_MASTER] = { .type = NL_A_NESTED, .optional = false },
+    [CTA_EXPECT_TUPLE] = { .type = NL_A_NESTED, .optional = false },
+    [CTA_EXPECT_MASK] = { .type = NL_A_NESTED, .optional = false },
+    [CTA_EXPECT_TIMEOUT] = { .type = NL_A_U32, .optional = false },
+    [CTA_EXPECT_ID] = { .type = NL_A_U32, .optional = false },
+    [CTA_EXPECT_HELP_NAME] = { .type = NL_A_STRING, .optional = true },
+    [CTA_EXPECT_ZONE] = { .type = NL_A_U16, .optional = true },
+    [CTA_EXPECT_FLAGS] = { .type = NL_A_U32, .optional = false },
+    [CTA_EXPECT_CLASS] = { .type = NL_A_U32, .optional = false },
+    [CTA_EXPECT_NAT] = { .type = NL_A_NESTED, .optional = true },
+    [CTA_EXPECT_FN] = { .type = NL_A_STRING, .optional = true },
+};
+
 /* Declarations for conntrack netlink dumping. */
 static void nl_msg_put_nfgenmsg(struct ofpbuf *msg, size_t expected_payload,
                                 int family, uint8_t subsystem, uint8_t cmd,
@@ -108,13 +131,22 @@ static bool nl_ct_parse_header_policy(struct ofpbuf *buf,
         enum nl_ct_event_type *event_type,
         uint8_t *nfgen_family,
         struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]);
+static bool nl_ct_parse_ct_exp_policy(struct ofpbuf *buf,
+        uint8_t *nfgen_family,
+        struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_exp_policy)]);
 
 static bool nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry,
         struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)],
         uint8_t nfgen_family);
+static bool nl_ct_exp_attrs_to_ct_dpif_exp_entry(
+        struct ct_dpif_exp_entry *entry,
+        struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_exp_policy)],
+        uint8_t nfgen_family);
 
 static bool nl_ct_put_ct_tuple(struct ofpbuf *, struct ct_dpif_tuple *,
                                enum ctattr_type);
+static bool nl_ct_put_ct_exp_tuple(struct ofpbuf *, struct ct_dpif_tuple *,
+                                   enum ctattr_expect);
 
 struct nl_ct_dump_state {
     struct nl_dump dump;
@@ -385,6 +417,51 @@ nl_ct_get_ct_state(struct ct_dpif_tuple *tuple, struct ct_dpif_entry *entry,
     }
 }
 
+/* Searches nf_conntrack's expectation table for 'tuple' in 'zone'.
+ * Sets 'exp' and returns 0 if we successfully find a match. Otherwise,
+ * returns non-zero errno. */
+int
+nl_ct_get_expect(struct ct_dpif_tuple *tuple, uint16_t zone,
+                    struct ct_dpif_exp_entry *exp)
+{
+    struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_exp_policy)];
+    struct ofpbuf request, *reply;
+    uint8_t nfgen_family;
+    int err;
+
+    ofpbuf_init(&request, NL_DUMP_BUFSIZE);
+
+    nl_msg_put_nfgenmsg(&request, 0, tuple->l3_type, NFNL_SUBSYS_CTNETLINK_EXP,
+                       IPCTNL_MSG_EXP_GET, NLM_F_REQUEST);
+    nl_msg_put_be16(&request, CTA_EXPECT_ZONE, htons(zone));
+    if (!nl_ct_put_ct_exp_tuple(&request, tuple, CTA_EXPECT_TUPLE)) {
+        ofpbuf_uninit(&request);
+        return EOPNOTSUPP;
+    }
+
+    err = nl_transact(NETLINK_NETFILTER, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (err) {
+        return err;
+    }
+
+    if (!nl_ct_parse_ct_exp_policy(reply, &nfgen_family, attrs)) {
+        goto out;
+    }
+
+    memset(exp, 0, sizeof(*exp));
+    if (!nl_ct_exp_attrs_to_ct_dpif_exp_entry(exp, attrs, nfgen_family)) {
+        goto out;
+    }
+
+    ofpbuf_delete(reply);
+    return 0;
+
+out:
+    ofpbuf_delete(reply);
+    return EOPNOTSUPP;
+}
+
 int
 nl_ct_get_info(struct ct_dpif_tuple *tuple, uint16_t zone,
                struct ct_dpif_info *info)
@@ -392,10 +469,17 @@ nl_ct_get_info(struct ct_dpif_tuple *tuple, uint16_t zone,
     struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)];
     struct ofpbuf request, *reply;
     struct ct_dpif_entry entry;
+    struct ct_dpif_exp_entry exp;
     enum nl_ct_event_type type;
     uint8_t nfgen_family;
     int err;
 
+    if (nl_ct_get_expect(tuple, zone, &exp) == 0) {
+        info->ct_state = CS_TRACKED | CS_NEW | CS_RELATED;
+        free(exp.helper.name);
+        return 0;
+    }
+
     ofpbuf_init(&request, NL_DUMP_BUFSIZE);
     nl_msg_put_nfgenmsg(&request, 0, tuple->l3_type, NFNL_SUBSYS_CTNETLINK,
                         IPCTNL_MSG_CT_GET, NLM_F_REQUEST);
@@ -721,6 +805,23 @@ nl_ct_put_ct_tuple(struct ofpbuf *buf, struct ct_dpif_tuple *tuple,
     return true;
 }
 
+static bool
+nl_ct_put_ct_exp_tuple(struct ofpbuf *buf, struct ct_dpif_tuple *tuple,
+                       enum ctattr_expect type)
+{
+    size_t offset = nl_msg_start_nested(buf, type);
+
+    if (type != CTA_EXPECT_TUPLE && type != CTA_EXPECT_MASTER) {
+        return false;
+    }
+    if (!nl_ct_put_tuple(buf, tuple)) {
+        return false;
+    }
+
+    nl_msg_end_nested(buf, offset);
+    return true;
+}
+
 /* Translate netlink TCP state to CT_DPIF_TCP state. */
 static uint8_t
 nl_ct_tcp_state_to_dpif(uint8_t state)
@@ -945,6 +1046,42 @@ nl_ct_parse_header_policy(struct ofpbuf *buf,
 }
 
 static bool
+nl_ct_parse_ct_exp_policy(struct ofpbuf *buf, uint8_t *nfgen_family,
+        struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_exp_policy)])
+{
+    struct nlmsghdr *nlh;
+    struct nfgenmsg *nfm;
+
+    nlh = ofpbuf_at(buf, 0, NLMSG_HDRLEN);
+    nfm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *nfm);
+    if (!nfm) {
+        VLOG_ERR_RL(&rl, "Received bad nfnl message (no nfgenmsg).");
+        return false;
+    }
+    if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_CTNETLINK_EXP) {
+        VLOG_ERR_RL(&rl, "Received non-conntrack message (subsystem: %u).",
+                 NFNL_SUBSYS_ID(nlh->nlmsg_type));
+        return false;
+    }
+    if (nfm->version != NFNETLINK_V0) {
+        VLOG_ERR_RL(&rl, "Received unsupported nfnetlink version (%u).",
+                 NFNL_MSG_TYPE(nfm->version));
+        return false;
+    }
+
+    if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof *nfm,
+                         nfnlgrp_conntrack_exp_policy, attrs,
+                         ARRAY_SIZE(nfnlgrp_conntrack_exp_policy))) {
+        VLOG_ERR_RL(&rl, "Received bad nfnl message (policy).");
+        return false;
+    }
+
+    *nfgen_family = nfm->nfgen_family;
+
+    return true;
+}
+
+static bool
 nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry,
         struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)],
         uint8_t nfgen_family)
@@ -1008,6 +1145,72 @@ nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry,
     return true;
 }
 
+static bool
+nl_ct_parse_exp_nat(struct nlattr *nla, struct ct_dpif_exp_entry *entry,
+                    uint16_t l3_type)
+{
+    static const struct nl_policy policy[] = {
+        [CTA_EXPECT_NAT_DIR] = { .type = NL_A_BE32, .optional = false },
+        [CTA_EXPECT_NAT_TUPLE] = { .type = NL_A_NESTED, .optional = false },
+    };
+    struct nlattr *attrs[ARRAY_SIZE(policy)];
+    bool parsed;
+
+    parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy));
+
+    if (parsed) {
+        entry->nat_dir = ntohl(nl_attr_get_be32(attrs[CTA_EXPECT_NAT_DIR]));
+        if (!nl_ct_parse_tuple(attrs[CTA_EXPECT_NAT_TUPLE], &entry->tuple_nat,
+                               l3_type)) {
+            return false;
+        }
+    } else {
+        VLOG_ERR_RL(&rl, "Could not parse nested expect NAT options. "
+                    "Possibly incompatible Linux kernel version.");
+    }
+    return parsed;
+}
+
+static bool
+nl_ct_exp_attrs_to_ct_dpif_exp_entry(struct ct_dpif_exp_entry *entry,
+        struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_exp_policy)],
+        uint8_t nfgen_family)
+{
+    if (!nl_ct_parse_tuple(attrs[CTA_EXPECT_TUPLE], &entry->tuple,
+                           nfgen_family)) {
+        return false;
+    }
+    if (!nl_ct_parse_tuple(attrs[CTA_EXPECT_MASK], &entry->tuple_mask,
+                           nfgen_family)) {
+        return false;
+    }
+    if (!nl_ct_parse_tuple(attrs[CTA_EXPECT_MASTER], &entry->tuple_master,
+                           nfgen_family)) {
+        return false;
+    }
+    if (attrs[CTA_EXPECT_NAT] &&
+        !nl_ct_parse_exp_nat(attrs[CTA_EXPECT_NAT], entry, nfgen_family)) {
+        return false;
+    }
+    if (attrs[CTA_EXPECT_TIMEOUT]) {
+        entry->timeout = ntohl(nl_attr_get_be32(attrs[CTA_EXPECT_TIMEOUT]));
+    }
+    if (attrs[CTA_EXPECT_ID]) {
+        entry->id = ntohl(nl_attr_get_be32(attrs[CTA_EXPECT_ID]));
+    }
+    if (attrs[CTA_EXPECT_FLAGS]) {
+        entry->exp_flags = ntohl(nl_attr_get_be32(attrs[CTA_EXPECT_FLAGS]));
+    }
+    if (attrs[CTA_EXPECT_CLASS]) {
+        entry->exp_class = ntohl(nl_attr_get_be32(attrs[CTA_EXPECT_CLASS]));
+    }
+    if (attrs[CTA_EXPECT_HELP_NAME]) {
+        entry->helper.name = xstrdup(nl_attr_get_string(
+                                attrs[CTA_EXPECT_HELP_NAME]));
+    }
+    return true;
+}
+
 bool
 nl_ct_parse_entry(struct ofpbuf *buf, struct ct_dpif_entry *entry,
                   enum nl_ct_event_type *event_type)
diff --git a/lib/netlink-conntrack.h b/lib/netlink-conntrack.h
index 86d3adab0403..4c5798bdefc8 100644
--- a/lib/netlink-conntrack.h
+++ b/lib/netlink-conntrack.h
@@ -45,6 +45,8 @@ int nl_ct_flush_zone(uint16_t zone);
 
 int nl_ct_get_info(struct ct_dpif_tuple *, uint16_t zone,
                    struct ct_dpif_info *info);
+int nl_ct_get_expect(struct ct_dpif_tuple *, uint16_t zone,
+                     struct ct_dpif_exp_entry *);
 
 bool nl_ct_parse_entry(struct ofpbuf *, struct ct_dpif_entry *,
                        enum nl_ct_event_type *);
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 7c5e9e3357e5..35a1c7ebc7d0 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -162,6 +162,9 @@ fi
 # Set HAVE_TCPDUMP
 find_command tcpdump
 
+# Set HAVE_CONNTRACK
+find_command conntrack
+
 CURL_OPT="-g -v --max-time 1 --retry 2 --retry-delay 1 --connect-timeout 1"
 
 # Turn off proxies.
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index ccad3fab0ca4..727f97f7a7dd 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -4113,6 +4113,143 @@ AT_CHECK([grep -c 'ct_state=est|rpl|trk' stdout], [0], [2
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([conntrack - ofproto/trace FTP])
+AT_SKIP_IF([test $HAVE_FTP = no])
+AT_SKIP_IF([test $HAVE_CONNTRACK = no])
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_ALG()
+CHECK_CT_DPIF_GET_INFO()
+OVS_TRAFFIC_VSWITCHD_START()
+
+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")
+
+dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from ns1->ns0.
+AT_DATA([flows1.txt], [dnl
+table=0,priority=1,action=drop
+table=0,priority=10,arp,action=normal
+table=0,priority=10,icmp,action=normal
+table=0,priority=100,in_port=1,tcp,action=ct(alg=ftp,commit),2
+table=0,priority=100,in_port=2,tcp,action=ct(table=1)
+table=1,in_port=2,tcp,ct_state=+trk+est,action=1
+table=1,in_port=2,tcp,ct_state=+trk+rel,action=1
+])
+
+dnl Similar policy but without allowing all traffic from ns0->ns1.
+AT_DATA([flows2.txt], [dnl
+table=0,priority=1,action=drop
+table=0,priority=10,arp,action=normal
+table=0,priority=10,icmp,action=normal
+
+dnl Allow outgoing TCP connections, and treat them as FTP
+table=0,priority=100,in_port=1,tcp,action=ct(table=1)
+table=1,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,alg=ftp),2
+table=1,in_port=1,tcp,ct_state=+trk+est,action=2
+
+dnl Allow incoming FTP data connections and responses to existing connections
+table=0,priority=100,in_port=2,tcp,action=ct(table=1)
+table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1
+table=1,in_port=2,tcp,ct_state=+trk+est,action=1
+table=1,in_port=2,tcp,ct_state=+trk-new+rel,action=1
+])
+
+AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows1.txt])
+
+OVS_START_L7([at_ns0], [ftp])
+OVS_START_L7([at_ns1], [ftp])
+
+dnl FTP requests from p1->p0 should fail due to network failure.
+dnl Try 3 times, in 1 second intervals.
+NS_CHECK_EXEC([at_ns1], [wget ftp://10.1.1.1 --no-passive-ftp  -t 3 -T 1 -v -o wget1.log], [4])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl
+])
+
+dnl FTP requests from p0->p1 should work fine.
+NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v -o wget0.log])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
+tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
+])
+
+dnl Test ofproto/trace
+DPORT=$(conntrack -L expect | grep 'src=10.1.1.2' | cut -d ' ' -f6 | cut -d '=' -f2)
+
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,dl_type=0x800,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_proto=6,tcp_src=12345,tcp_dst=$DPORT"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+AT_CHECK([grep -c 'ct_state=new|rel|trk' stdout], [0], [2
+])
+
+dnl Try the second set of flows.
+AT_CHECK([ovs-ofctl --bundle replace-flows br0 flows2.txt])
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+dnl FTP requests from p1->p0 should fail due to network failure.
+dnl Try 3 times, in 1 second intervals.
+NS_CHECK_EXEC([at_ns1], [wget ftp://10.1.1.1 --no-passive-ftp  -t 3 -T 1 -v -o wget1.log], [4])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.1)], [0], [dnl
+])
+
+dnl Active FTP requests from p0->p1 should work fine.
+NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 --no-passive-ftp -t 3 -T 1 --retry-connrefused -v -o wget0-1.log])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
+tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
+tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>)
+])
+
+dnl Test ofproto/trace
+ovs-appctl dpctl/dump-conntrack
+CONNTRACK_INFO=$(ovs-appctl dpctl/dump-conntrack | grep -v 'dport=21)' | grep '(src=10.1.1.2,dst=10.1.1.1,')
+SPORT=$(echo $CONNTRACK_INFO | cut -d ',' -f4 | cut -d '=' -f2)
+DPORT=$(echo $CONNTRACK_INFO | cut -d ',' -f5 | cut -d ')' -f1 | cut -d '=' -f2)
+
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,dl_type=0x800,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_proto=6,tcp_src=$SPORT,tcp_dst=$DPORT"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+AT_CHECK([grep -c 'ct_state=est|rel|trk' stdout], [0], [2
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x800,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_proto=6,tcp_src=$DPORT,tcp_dst=$SPORT"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 3
+])
+AT_CHECK([grep -c 'ct_state=est|rel|rpl|trk' stdout], [0], [2
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+dnl Passive FTP requests from p0->p1 should work fine.
+NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.2 -t 3 -T 1 --retry-connrefused -v -o wget0-2.log])
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
+tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
+])
+
+dnl Test ofproto/trace
+ovs-appctl dpctl/dump-conntrack
+CONNTRACK_INFO=$(ovs-appctl dpctl/dump-conntrack | grep -v 'dport=21)' | grep '(src=10.1.1.1,dst=10.1.1.2,')
+SPORT=$(echo $CONNTRACK_INFO | cut -d ',' -f4 | cut -d '=' -f2)
+DPORT=$(echo $CONNTRACK_INFO | cut -d ',' -f5 | cut -d ')' -f1 | cut -d '=' -f2)
+
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,dl_type=0x800,nw_src=10.1.1.2,nw_dst=10.1.1.1,nw_proto=6,tcp_src=$DPORT,tcp_dst=$SPORT"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+AT_CHECK([grep -c 'ct_state=est|rel|rpl|trk' stdout], [0], [2
+])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x800,nw_src=10.1.1.1,nw_dst=10.1.1.2,nw_proto=6,tcp_src=$SPORT,tcp_dst=$DPORT"], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 3
+])
+AT_CHECK([grep -c 'ct_state=est|rel|trk' stdout], [0], [2
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([conntrack - ofproto/trace SNAT])
 AT_SKIP_IF([test $HAVE_NC = no])
 CHECK_CONNTRACK()
-- 
2.7.4



More information about the dev mailing list