[ovs-dev] [PATCH 5/9] dpif-netlink: Implement ct_get_info

Greg Rose gvrose8192 at gmail.com
Fri Sep 1 16:03:42 UTC 2017


On 08/25/2017 03:51 PM, Yi-Hung Wei wrote:
> This patch implements ct_get_info() in dpif-netlink. It uses
> NFNL_SUBSYS_CTNETLINK netlink subsystem to query conntrack info from
> kernel datapath. Then, ofproto/trace can use the ct_get_info() to derive
> the ct_state of the traced flow. System traffic tests are provided to verify
> that ofproto/trace can get the correct ct_state for IPv4 TCP/UDP, and NAT
> traffic.
>
> Signed-off-by: Yi-Hung Wei <yihung.wei at gmail.com>
> ---
>   lib/dpif-netlink.c               |  10 +-
>   lib/netlink-conntrack.c          | 204 +++++++++++++++++++++++++++++++++++++++
>   lib/netlink-conntrack.h          |   3 +
>   tests/system-kmod-macros.at      |   6 ++
>   tests/system-traffic.at          | 181 ++++++++++++++++++++++++++++++++++
>   tests/system-userspace-macros.at |   9 ++
>   6 files changed, 412 insertions(+), 1 deletion(-)
>
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index 122c59614f5a..cc22cc6068c2 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -2901,6 +2901,14 @@ dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone)
>       }
>   }
>
> +static int
> +dpif_netlink_ct_get_info(struct dpif *dpif OVS_UNUSED,
> +                         struct ct_dpif_tuple *tuple, uint16_t zone,
> +                         struct ct_dpif_info *info)
> +{
> +    return nl_ct_get_info(tuple, zone, info);
> +}
> +
>   
>   /* Meters */
>   static void
> @@ -2986,7 +2994,7 @@ const struct dpif_class dpif_netlink_class = {
>       dpif_netlink_ct_dump_next,
>       dpif_netlink_ct_dump_done,
>       dpif_netlink_ct_flush,
> -    NULL,                       /* ct_get_info */
> +    dpif_netlink_ct_get_info,
>       dpif_netlink_meter_get_features,
>       dpif_netlink_meter_set,
>       dpif_netlink_meter_get,
> diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c
> index ac48b15bde90..93cd0ac2c34f 100644
> --- a/lib/netlink-conntrack.c
> +++ b/lib/netlink-conntrack.c
> @@ -18,6 +18,7 @@
>
>   #include "netlink-conntrack.h"
>
> +#include <errno.h>
>   #include <linux/netfilter/nfnetlink.h>
>   #include <linux/netfilter/nfnetlink_conntrack.h>
>   #include <linux/netfilter/nf_conntrack_common.h>
> @@ -112,6 +113,9 @@ 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_put_ct_tuple(struct ofpbuf *, struct ct_dpif_tuple *,
> +                               enum ctattr_type);
> +
>   struct nl_ct_dump_state {
>       struct nl_dump dump;
>       struct ofpbuf buf;
> @@ -325,6 +329,125 @@ nl_ct_flush_zone(uint16_t flush_zone)
>   }
>   #endif
>
> +static bool
> +nl_ct_cmp_ct_dpif_tuple(struct ct_dpif_tuple *t1, struct ct_dpif_tuple *t2)
> +{
> +    if (t1->l3_type != t2->l3_type || t1->ip_proto != t2->ip_proto) {
> +        return false;
> +    }
> +
> +    if (t1->l3_type == AF_INET) {
> +        if (t1->src.ip != t2->src.ip || t1->dst.ip != t2->dst.ip) {
> +            return false;
> +        }
> +    } else {
> +        if (memcmp(&t1->src.in6, &t2->src.in6, sizeof t1->src.in6) ||
> +            memcpy(&t1->dst.in6, &t2->dst.in6, sizeof t1->dst.in6)) {

This memcpy doesn't seem right...

> +            return false;
> +        }
> +    }
> +
> +    if (t1->ip_proto == IPPROTO_ICMP || t1->ip_proto == IPPROTO_ICMPV6) {
> +        if (t1->icmp_id != t2->icmp_id || t1->icmp_type != t2->icmp_type ||
> +            t1->icmp_code != t2->icmp_code) {
> +            return false;
> +        }
> +    } else {
> +        if (t1->src_port != t2->src_port || t1->dst_port != t2->dst_port) {
> +            return false;
> +        }
> +    }
> +
> +    return true;
> +}
> +
> +static void
> +nl_ct_get_ct_state(struct ct_dpif_tuple *tuple, struct ct_dpif_entry *entry,
> +                   uint32_t *ct_state)
> +{
> +    if (entry->status & IPS_SEEN_REPLY) {
> +        *ct_state |= CS_ESTABLISHED;
> +    }
> +    if (!nl_ct_cmp_ct_dpif_tuple(tuple, &entry->tuple_orig)) {
> +        *ct_state |= CS_REPLY_DIR;
> +    }
> +    if (entry->tuple_master.l3_type) {
> +        *ct_state |= CS_RELATED;
> +    }
> +    if (entry->status & IPS_SRC_NAT_DONE) {
> +        *ct_state |= *ct_state & CS_REPLY_DIR ? CS_DST_NAT : CS_SRC_NAT;
> +    }
> +    if (entry->status & IPS_DST_NAT_DONE) {
> +        *ct_state |= *ct_state & CS_REPLY_DIR ? CS_SRC_NAT : CS_DST_NAT;
> +    }
> +    if (*ct_state == 0) {
> +        *ct_state = CS_NEW;
> +    }
Where do these CS_* #defines come from?  I couldn't find their definition in the OVS source tree or in the Linux kernel
lexer.  I'm only asking for my own education.  ;)

> +}
> +
> +int
> +nl_ct_get_info(struct ct_dpif_tuple *tuple, uint16_t zone,
> +               struct ct_dpif_info *info)
> +{
> +    struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)];
> +    struct ofpbuf request, *reply;
> +    struct ct_dpif_entry entry;
> +    enum nl_ct_event_type type;
> +    uint8_t nfgen_family;
> +    int err;
> +
> +    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);
> +    nl_msg_put_be16(&request, CTA_ZONE, htons(zone));
> +    if (!nl_ct_put_ct_tuple(&request, tuple, CTA_TUPLE_ORIG)) {
> +        ofpbuf_uninit(&request);
> +        return EOPNOTSUPP;
> +    }
> +
> +    err = nl_transact(NETLINK_NETFILTER, &request, &reply);
> +    ofpbuf_uninit(&request);
> +
> +    if (err == ENOENT) {
> +        info->ct_state = CS_TRACKED | CS_NEW;
> +        return 0;
> +    } else if (err) {
> +        struct ds s = DS_EMPTY_INITIALIZER;
> +
> +        ct_dpif_format_tuple(&s, tuple);
> +        VLOG_WARN_RL(&rl, "Could not get conntrack info for tuple %s, "
> +                          "errono %d\n", ds_cstr(&s), err);
> +        ds_destroy(&s);
> +        return EOPNOTSUPP;
> +    }
> +
> +    if (!nl_ct_parse_header_policy(reply, &type, &nfgen_family, attrs)) {
> +        goto out;
> +    }
> +
> +    uint16_t entry_zone = attrs[CTA_ZONE] ?
> +                          ntohs(nl_attr_get_be16(attrs[CTA_ZONE])) : 0;
> +    if (entry_zone != zone) {
> +        VLOG_WARN_RL(&rl, "Received invalid zone number %d\n", entry_zone);
> +        goto out;
> +    }
> +
> +    memset(&entry, 0, sizeof(entry));
> +    if (nl_ct_attrs_to_ct_dpif_entry(&entry, attrs, nfgen_family) == false) {
> +        goto out;
> +    }
> +
> +    nl_ct_get_ct_state(tuple, &entry, &info->ct_state);
> +    info->ct_state |= CS_TRACKED;
> +
> +    ofpbuf_delete(reply);
> +    return 0;
> +
> +out:
> +    ofpbuf_delete(reply);
> +    return EOPNOTSUPP;
> +}
> +
>   /* Conntrack netlink parsing. */
>
>   static bool
> @@ -517,6 +640,87 @@ nl_ct_parse_tuple(struct nlattr *nla, struct ct_dpif_tuple *tuple,
>       return parsed;
>   }
>
> +static bool
> +nl_ct_put_tuple_ip(struct ofpbuf *buf, struct ct_dpif_tuple *tuple)
> +{
> +    size_t offset = nl_msg_start_nested(buf, CTA_TUPLE_IP);
> +
> +    if (tuple->l3_type == AF_INET) {
> +        nl_msg_put_be32(buf, CTA_IP_V4_SRC, tuple->src.ip);
> +        nl_msg_put_be32(buf, CTA_IP_V4_DST, tuple->dst.ip);
> +    } else if (tuple->l3_type == AF_INET6) {
> +        nl_msg_put_in6_addr(buf, CTA_IP_V6_SRC, &tuple->src.in6);
> +        nl_msg_put_in6_addr(buf, CTA_IP_V6_DST, &tuple->dst.in6);
> +    } else {
> +        VLOG_WARN_RL(&rl, "Unsupported IP protocol: %"PRIu16".",
> +                     tuple->l3_type);
> +        return false;
> +    }
> +
> +    nl_msg_end_nested(buf, offset);
> +    return true;
> +}
> +
> +static bool
> +nl_ct_put_tuple_proto(struct ofpbuf *buf, struct ct_dpif_tuple *tuple)
> +{
> +    size_t offset = nl_msg_start_nested(buf, CTA_TUPLE_PROTO);
> +
> +    nl_msg_put_u8(buf, CTA_PROTO_NUM, tuple->ip_proto);
> +
> +    if (tuple->l3_type == AF_INET && tuple->ip_proto == IPPROTO_ICMP) {
> +        nl_msg_put_be16(buf, CTA_PROTO_ICMP_ID, tuple->icmp_id);
> +        nl_msg_put_u8(buf, CTA_PROTO_ICMP_TYPE, tuple->icmp_type);
> +        nl_msg_put_u8(buf, CTA_PROTO_ICMP_CODE, tuple->icmp_code);
> +    } else if (tuple->l3_type == AF_INET6 &&
> +               tuple->ip_proto == IPPROTO_ICMPV6) {
> +        nl_msg_put_be16(buf, CTA_PROTO_ICMPV6_ID, tuple->icmp_id);
> +        nl_msg_put_u8(buf, CTA_PROTO_ICMPV6_TYPE, tuple->icmp_type);
> +        nl_msg_put_u8(buf, CTA_PROTO_ICMPV6_CODE, tuple->icmp_code);
> +    } else if (tuple->ip_proto == IPPROTO_TCP ||
> +               tuple->ip_proto == IPPROTO_UDP) {
> +        nl_msg_put_be16(buf, CTA_PROTO_SRC_PORT, tuple->src_port);
> +        nl_msg_put_be16(buf, CTA_PROTO_DST_PORT, tuple->dst_port);
> +    } else {
> +        VLOG_WARN_RL(&rl, "Unsupported L4 protocol: %"PRIu8".",
> +                     tuple->ip_proto);
> +        return false;
> +    }
> +
> +    nl_msg_end_nested(buf, offset);
> +    return true;
> +}
> +
> +static bool
> +nl_ct_put_tuple(struct ofpbuf *buf, struct ct_dpif_tuple *tuple)
> +{
> +    if (!nl_ct_put_tuple_ip(buf, tuple)) {
> +        return false;
> +    }
> +    if (!nl_ct_put_tuple_proto(buf, tuple)) {
> +        return false;
> +    }
> +    return true;
> +}
> +
> +static bool
> +nl_ct_put_ct_tuple(struct ofpbuf *buf, struct ct_dpif_tuple *tuple,
> +                   enum ctattr_type type)
> +{
> +    size_t offset = nl_msg_start_nested(buf, type);

Shouldn't we wait to start a nested netlink message after the next check?

> +
> +    if (type != CTA_TUPLE_ORIG && type != CTA_TUPLE_REPLY &&
> +        type != CTA_TUPLE_MASTER) {
> +        return false;
> +    }
This statement seems really odd to me... To my sensibility the logic is inverted.
But to make sure I've got this right you''re checking to see if type is
equal to any of those values before continuing?  So any other
ct attribute type is rejected?

I think you should call nl_msg_start_nested() after this check.

> +    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)
> diff --git a/lib/netlink-conntrack.h b/lib/netlink-conntrack.h
> index a95aa69a4cde..86d3adab0403 100644
> --- a/lib/netlink-conntrack.h
> +++ b/lib/netlink-conntrack.h
> @@ -43,6 +43,9 @@ int nl_ct_dump_done(struct nl_ct_dump_state *);
>   int nl_ct_flush(void);
>   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);
> +
>   bool nl_ct_parse_entry(struct ofpbuf *, struct ct_dpif_entry *,
>                          enum nl_ct_event_type *);
>   void nl_ct_format_event_entry(const struct ct_dpif_entry *,
> diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
> index 27498341a9f8..60751c215c3e 100644
> --- a/tests/system-kmod-macros.at
> +++ b/tests/system-kmod-macros.at
> @@ -96,3 +96,9 @@ m4_define([CHECK_CONNTRACK_LOCAL_STACK])
>   # always supports NAT, so no check is needed.
>   #
>   m4_define([CHECK_CONNTRACK_NAT])
> +
> +# CHECK_CT_DPIF_GET_INFO()
> +#
> +# Perform requirements checks for running ofproto/trace tests. The kernel
> +# datapath does support ct_dpif_get_info(), so no check is needed.
> +m4_define([CHECK_CT_DPIF_GET_INFO])
> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> index 522eaa615834..ccad3fab0ca4 100644
> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -3996,6 +3996,187 @@ ovs-ofctl -O OpenFlow15 dump-group-stats br0
>   OVS_TRAFFIC_VSWITCHD_STOP
>   AT_CLEANUP
>
> +AT_SETUP([conntrack - ofproto/trace])
> +AT_SKIP_IF([test $HAVE_NC = no])
> +CHECK_CONNTRACK()
> +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 Test IPv4 TCP traffic
> +AT_DATA([flows.txt], [dnl
> +table=0,priority=100,arp,action=normal
> +table=0,priority=10,in_port=1,tcp,action=ct(table=1,zone=1)
> +table=0,priority=10,in_port=2,tcp,ct_state=-trk,action=ct(table=1,zone=1)
> +
> +table=1,priority=10,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,zone=1),2
> +table=1,priority=10,in_port=1,tcp,ct_state=+trk+est,action=2
> +table=1,priority=10,in_port=2,tcp,ct_state=+trk+new,action=drop
> +table=1,priority=10,in_port=2,tcp,ct_state=+trk+est+rpl,action=1
> +])
> +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
> +
> +dnl Start TCP server in ns1
> +NETNS_DAEMONIZE([at_ns1], [nc -l 22222], [nc_tcp_server.pid])
> +dnl use this file as payload file for ncat
> +AT_CHECK([dd if=/dev/urandom of=payload100.bin bs=100 count=1 2> /dev/null])
> +on_exit 'rm -f payload100.bin'
> +
> +dnl Run ofproto/trace before connection is established.
> +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=33333,tcp_dst=22222"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: ct(commit,zone=1),3
> +])
> +AT_CHECK([grep -c 'ct_state=new|trk' stdout], [0], [2
> +])
> +
> +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=22222,tcp_dst=33333"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: drop
> +])
> +AT_CHECK([grep -c 'ct_state=new|trk' stdout], [0], [2
> +])
> +
> +dnl Establish TCP connection.
> +NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -p 33333 10.1.1.2 22222 < payload100.bin])
> +
> +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=33333,tcp_dst=22222"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 3
> +])
> +AT_CHECK([grep -c 'ct_state=est|trk' stdout], [0], [2
> +])
> +
> +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=22222,tcp_dst=33333"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 2
> +])
> +AT_CHECK([grep -c 'ct_state=est|rpl|trk' stdout], [0], [2
> +])
> +
> +
> +dnl Test IPv4 UDP traffic
> +AT_DATA([flows.txt], [dnl
> +table=0,priority=100,arp,action=normal
> +table=0,priority=10,in_port=1,udp,action=ct(table=1,zone=1)
> +table=0,priority=10,in_port=2,udp,ct_state=-trk,action=ct(table=1,zone=1)
> +table=0,priority=1,in_port=2,udp,action=drop
> +
> +table=1,priority=10,in_port=1,udp,ct_state=+trk+new,action=ct(commit,zone=1),2
> +table=1,priority=10,in_port=1,udp,ct_state=+trk+est,action=2
> +table=1,priority=10,in_port=2,udp,ct_state=+trk+new,action=drop
> +table=1,priority=10,in_port=2,udp,ct_state=+trk+est+rpl,action=1
> +])
> +
> +AT_CHECK([ovs-ofctl del-flows br0])
> +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
> +
> +dnl Start UDP server in ns1
> +NETNS_DAEMONIZE([at_ns1], [nc -ul 44444 < payload100.bin], [nc_udp_server.pid])
> +
> +dnl Run ofproto/trace before connection is established.
> +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=17,udp_src=33333,udp_dst=44444"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: ct(commit,zone=1),3
> +])
> +AT_CHECK([grep -c 'ct_state=new|trk' stdout], [0], [2
> +])
> +
> +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=17,udp_src=44444,udp_dst=33333"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: drop
> +])
> +AT_CHECK([grep -c 'ct_state=new|trk' stdout], [0], [2
> +])
> +
> +dnl Establish UDP connection.
> +NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -u -p 33333 10.1.1.2 44444 < payload100.bin], [0], [stdout])
> +
> +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=17,udp_src=33333,udp_dst=44444"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 3
> +])
> +AT_CHECK([grep -c 'ct_state=est|trk' stdout], [0], [2
> +])
> +
> +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=17,udp_src=44444,udp_dst=33333"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 2
> +])
> +AT_CHECK([grep -c 'ct_state=est|rpl|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()
> +CHECK_CONNTRACK_NAT()
> +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")
> +NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address 80:88:88:88:88:88])
> +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([flows.txt], [dnl
> +in_port=1,ip,ct_state=-trk,action=ct(commit,table=1,zone=1,nat(src=10.1.1.240))
> +in_port=2,ct_state=-trk,ip,action=ct(table=1,zone=1,nat)
> +dnl
> +dnl ARP
> +priority=100 arp arp_op=1 action=move:OXM_OF_ARP_TPA[[]]->NXM_NX_REG2[[]],resubmit(,8),goto_table:10
> +priority=10 arp action=normal
> +priority=0,action=drop
> +dnl
> +table=1,in_port=1,ip,ct_state=+trk+snat,action=2
> +table=1,in_port=2,ct_state=+trk+rpl+est+dnat,ct_zone=1,ip,action=1
> +dnl
> +dnl MAC resolution table for IP in reg2, stores mac in OXM_OF_PKT_REG0
> +table=8,reg2=0x0a0101f0/0xfffffff0,action=load:0x808888888888->OXM_OF_PKT_REG0[[]]
> +table=8,priority=0,action=load:0->OXM_OF_PKT_REG0[[]]
> +dnl ARP responder mac filled in at OXM_OF_PKT_REG0, or 0 for normal action.
> +dnl TPA IP in reg2.
> +dnl Swaps the fields of the ARP message to turn a query to a response.
> +table=10 priority=100 arp xreg0=0 action=normal
> +table=10 priority=10,arp,arp_op=1,action=load:2->OXM_OF_ARP_OP[[]],move:OXM_OF_ARP_SHA[[]]->OXM_OF_ARP_THA[[]],move:OXM_OF_PKT_REG0[[0..47]]->OXM_OF_ARP_SHA[[]],move:OXM_OF_ARP_SPA[[]]->OXM_OF_ARP_TPA[[]],move:NXM_NX_REG2[[]]->OXM_OF_ARP_SPA[[]],move:NXM_OF_ETH_SRC[[]]->NXM_OF_ETH_DST[[]],move:OXM_OF_PKT_REG0[[0..47]]->NXM_OF_ETH_SRC[[]],move:NXM_OF_IN_PORT[[]]->NXM_NX_REG3[[0..15]],load:0->NXM_OF_IN_PORT[[]],output:NXM_NX_REG3[[0..15]]
> +table=10 priority=0 action=drop
> +])
> +
> +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
> +
> +dnl Start TCP server in ns1
> +NETNS_DAEMONIZE([at_ns1], [nc -l 22222], [nc_tcp_server.pid])
> +dnl use this file as payload file for ncat
> +AT_CHECK([dd if=/dev/urandom of=payload100.bin bs=100 count=1 2> /dev/null])
> +on_exit 'rm -f payload100.bin'
> +dnl Establish TCP connection.
> +NS_CHECK_EXEC([at_ns0], [nc $NC_EOF_OPT -p 33333 10.1.1.2 22222 < payload100.bin])
> +
> +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=33333,tcp_dst=22222"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 3
> +])
> +AT_CHECK([grep -c 'ct_state=est|trk|snat' stdout], [0], [2
> +])
> +
> +AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=2,dl_type=0x800,nw_src=10.1.1.2,nw_dst=10.1.1.240,nw_proto=6,tcp_src=22222,tcp_dst=33333"], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0],
> +  [Datapath actions: 2
> +])
> +AT_CHECK([grep -c 'ct_state=est|rpl|trk|dnat' stdout], [0], [2
> +])
> +
> +OVS_TRAFFIC_VSWITCHD_STOP
> +AT_CLEANUP
> +
>   AT_BANNER([802.1ad])
>
>   AT_SETUP([802.1ad - vlan_limit])
> diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
> index f3337f04499a..b2c47c7fea1b 100644
> --- a/tests/system-userspace-macros.at
> +++ b/tests/system-userspace-macros.at
> @@ -99,3 +99,12 @@ m4_define([CHECK_CONNTRACK_LOCAL_STACK],
>   # datapath supports NAT.
>   #
>   m4_define([CHECK_CONNTRACK_NAT])
> +
> +# CHECK_CT_DPIF_GET_INFO()
> +#
> +# Perform requirements checks for running ofproto/trace tests. The userspace
> +# datapath does not support ct_dpif_get_info().
> +m4_define([CHECK_CT_DPIF_GET_INFO],
> +[
> +    AT_SKIP_IF([:])
> +])
>



More information about the dev mailing list