[ovs-dev] [PATCH 9/9] ofproto/trace: Support ct_mark and ct_label in --ct-next

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


Previously, --ct-next option in ofproto/trace only supports specifying
ct_state. This patch adds support of ct_mark and ct_label.

Signed-off-by: Yi-Hung Wei <yihung.wei at gmail.com>
---
 lib/ct-dpif.c                |  7 ++++
 lib/ct-dpif.h                |  3 ++
 lib/netlink-conntrack.c      |  5 +++
 lib/odp-util.c               |  3 +-
 lib/odp-util.h               |  1 +
 ofproto/ofproto-dpif-trace.c | 98 ++++++++++++++++++++++++++++----------------
 ofproto/ofproto-dpif-trace.h |  7 ++--
 ofproto/ofproto-unixctl.man  | 16 +++++---
 tests/ofproto-dpif.at        | 12 +++---
 tests/system-traffic.at      |  6 +--
 10 files changed, 103 insertions(+), 55 deletions(-)

diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index 91388dd6681b..07c549707303 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -144,6 +144,13 @@ ct_dpif_format_info(const struct ct_dpif_info *info, struct ds *output)
     format_flags(&s, ct_state_to_string, info->ct_state, '|');
     ds_put_format(output, "ct_state=%s", ds_cstr(&s));
     ds_destroy(&s);
+
+    ds_put_format(output, ",ct_mark=%#"PRIx32, info->ct_mark);
+    if (info->have_label) {
+        ovs_be128 value = hton128(info->ct_label);
+        ds_put_cstr(output, ",ct_label=");
+        ds_put_hex(output, &value, sizeof value);
+    }
 }
 
 void
diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index 9122e8cd752b..9d50db920e6e 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -175,6 +175,9 @@ struct ct_dpif_entry {
 
 struct ct_dpif_info {
     uint32_t ct_state;
+    uint32_t ct_mark;
+    bool have_label;
+    ovs_u128 ct_label;
 };
 
 struct ct_dpif_exp_entry {
diff --git a/lib/netlink-conntrack.c b/lib/netlink-conntrack.c
index de1e467dc61a..57da43f77135 100644
--- a/lib/netlink-conntrack.c
+++ b/lib/netlink-conntrack.c
@@ -523,6 +523,11 @@ nl_ct_get_info(struct ct_dpif_tuple *tuple, uint16_t zone,
 
     nl_ct_get_ct_state(tuple, &entry, &info->ct_state);
     info->ct_state |= CS_TRACKED;
+    info->ct_mark = entry.mark;
+    if (entry.have_labels == true) {
+        info->have_label = true;
+        memcpy(&info->ct_label, &entry.labels, sizeof(info->ct_label));
+    }
 
     ofpbuf_delete(reply);
     return 0;
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 4f1499ed4a47..c9669ca4572a 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -88,7 +88,6 @@ static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[]
                                                  const struct nlattr *key);
 static void format_u128(struct ds *d, const ovs_32aligned_u128 *key,
                         const ovs_32aligned_u128 *mask, bool verbose);
-static int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask);
 
 static int parse_odp_action(const char *s, const struct simap *port_names,
                             struct ofpbuf *actions);
@@ -3527,7 +3526,7 @@ format_u128(struct ds *ds, const ovs_32aligned_u128 *key,
  * If either the value or mask is larger than 64 bits, the string must
  * be in hexadecimal.
  */
-static int
+int
 scan_u128(const char *s_, ovs_u128 *value, ovs_u128 *mask)
 {
     char *s = CONST_CAST(char *, s_);
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 27c2ab4f0111..c2633797284b 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -344,4 +344,5 @@ void odp_put_push_eth_action(struct ofpbuf *odp_actions,
                              const struct eth_addr *eth_src,
                              const struct eth_addr *eth_dst);
 
+int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask);
 #endif /* odp-util.h */
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index d00d41a89cab..a6844b205f56 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -29,7 +29,7 @@
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
                           const struct dp_packet *packet,
                           const struct ofpact[], size_t ofpacts_len,
-                          struct ovs_list *next_ct_states,
+                          struct ovs_list *next_cts,
                           struct ds *);
 static void oftrace_node_destroy(struct oftrace_node *);
 
@@ -124,20 +124,19 @@ oftrace_recirc_node_destroy(struct oftrace_recirc_node *node)
 }
 
 static void
-oftrace_push_ct_state(struct ovs_list *next_ct_states, uint32_t ct_state)
+oftrace_push_ct_info(struct ovs_list *next_cts, struct ct_dpif_info info)
 {
-    struct oftrace_next_ct_state *next_ct_state =
-        xmalloc(sizeof *next_ct_state);
-    next_ct_state->state = ct_state;
-    ovs_list_push_back(next_ct_states, &next_ct_state->node);
+    struct oftrace_next_ct *next_ct = xmalloc(sizeof *next_ct);
+    next_ct->info = info;
+    ovs_list_push_back(next_cts, &next_ct->node);
 }
 
 static void
-oftrace_pop_ct_state(struct ovs_list *next_ct_states, uint32_t *ct_state)
+oftrace_pop_ct_info(struct ovs_list *next_cts, struct ct_dpif_info *info)
 {
-    struct oftrace_next_ct_state *s;
-    LIST_FOR_EACH_POP (s, node, next_ct_states) {
-        *ct_state = s->state;
+    struct oftrace_next_ct *s;
+    LIST_FOR_EACH_POP (s, node, next_cts) {
+        *info = s->info;
         free(s);
         return;
     }
@@ -187,10 +186,12 @@ oftrace_node_print_details(struct ds *output,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_oftrace_options(int argc, const char *argv[],
-                      struct ovs_list *next_ct_states)
+                      struct ovs_list *next_cts)
 {
     int k;
+    char *name, *value, *arg;
     struct ds ds = DS_EMPTY_INITIALIZER;
+    struct ct_dpif_info info;
 
     for (k = 0; k < argc; k++) {
         if (!strncmp(argv[k], "--ct-next", 9)) {
@@ -198,14 +199,36 @@ parse_oftrace_options(int argc, const char *argv[],
                 return xasprintf("Missing argument for option %s", argv[k]);
             }
 
-            uint32_t ct_state;
-            if (!parse_ct_state(argv[++k], 0, "|", &ct_state, &ds)) {
-                return ds_steal_cstr(&ds);
-            }
-            if (!validate_ct_state(ct_state, &ds)) {
-                return ds_steal_cstr(&ds);
+            memset(&info, 0, sizeof(info));
+            arg = CONST_CAST(char *, argv[++k]);
+
+            while (ofputil_parse_key_value(&arg, &name, &value)) {
+                if (!*value) {
+                    return xasprintf("Field %s missing value", name);
+                }
+
+                if (!strcmp(name, "ct_state")) {
+                    if (!parse_ct_state(value, 0, "|", &info.ct_state, &ds)) {
+                        return ds_steal_cstr(&ds);
+                    }
+                    if (!validate_ct_state(info.ct_state, &ds)) {
+                        return ds_steal_cstr(&ds);
+                    }
+                } else if (!strcmp(name, "ct_mark")) {
+                    char *err = str_to_u32(value, &info.ct_mark);
+                    if (err) {
+                        free(err);
+                        return xasprintf("Failed to parse ct_mark");
+                    }
+                } else if (!strcmp(name, "ct_label")) {
+                    int err = scan_u128(value, &info.ct_label, NULL);
+                    if (err < 0) {
+                        return xasprintf("Failed to parse ct_label");
+                    }
+                    info.have_label = true;
+                }
             }
-            oftrace_push_ct_state(next_ct_states, ct_state);
+            oftrace_push_ct_info(next_cts, info);
         } else {
             return xasprintf("Invalid option %s", argv[k]);
         }
@@ -227,7 +250,7 @@ static char * OVS_WARN_UNUSED_RESULT
 parse_flow_and_packet(int argc, const char *argv[],
                       struct ofproto_dpif **ofprotop, struct flow *flow,
                       struct dp_packet **packetp,
-                      struct ovs_list *next_ct_states)
+                      struct ovs_list *next_cts)
 {
     const struct dpif_backer *backer = NULL;
     const char *error = NULL;
@@ -268,7 +291,7 @@ parse_flow_and_packet(int argc, const char *argv[],
         }
 
         m_err = parse_oftrace_options(argc - first_option, argv + first_option,
-                                      next_ct_states);
+                                      next_cts);
         if (m_err) {
             goto exit;
         }
@@ -407,11 +430,11 @@ exit:
 }
 
 static void
-free_ct_states(struct ovs_list *ct_states)
+free_cts(struct ovs_list *cts)
 {
-    while (!ovs_list_is_empty(ct_states)) {
-        uint32_t state;
-        oftrace_pop_ct_state(ct_states, &state);
+    while (!ovs_list_is_empty(cts)) {
+        struct ct_dpif_info info;
+        oftrace_pop_ct_info(cts, &info);
     }
 }
 
@@ -423,16 +446,15 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
     struct dp_packet *packet;
     char *error;
     struct flow flow;
-    struct ovs_list next_ct_states = OVS_LIST_INITIALIZER(&next_ct_states);
+    struct ovs_list next_cts = OVS_LIST_INITIALIZER(&next_cts);
 
     error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet,
-                                  &next_ct_states);
+                                  &next_cts);
     if (!error) {
         struct ds result;
 
         ds_init(&result);
-        ofproto_trace(ofproto, &flow, packet, NULL, 0, &next_ct_states,
-                      &result);
+        ofproto_trace(ofproto, &flow, packet, NULL, 0, &next_cts, &result);
         unixctl_command_reply(conn, ds_cstr(&result));
         ds_destroy(&result);
         dp_packet_delete(packet);
@@ -440,7 +462,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
         unixctl_command_reply_error(conn, error);
         free(error);
     }
-    free_ct_states(&next_ct_states);
+    free_cts(&next_cts);
 }
 
 static void
@@ -455,7 +477,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     struct ds result;
     struct match match;
     uint16_t in_port;
-    struct ovs_list next_ct_states = OVS_LIST_INITIALIZER(&next_ct_states);
+    struct ovs_list next_cts = OVS_LIST_INITIALIZER(&next_cts);
 
     /* Three kinds of error return values! */
     enum ofperr retval;
@@ -486,7 +508,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     }
 
     error = parse_flow_and_packet(argc, argv, &ofproto, &match.flow, &packet,
-                                  &next_ct_states);
+                                  &next_cts);
     if (error) {
         unixctl_command_reply_error(conn, error);
         free(error);
@@ -534,14 +556,14 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     }
 
     ofproto_trace(ofproto, &match.flow, packet,
-                  ofpacts.data, ofpacts.size, &next_ct_states, &result);
+                  ofpacts.data, ofpacts.size, &next_cts, &result);
     unixctl_command_reply(conn, ds_cstr(&result));
 
 exit:
     ds_destroy(&result);
     dp_packet_delete(packet);
     ofpbuf_uninit(&ofpacts);
-    free_ct_states(&next_ct_states);
+    free_cts(&next_cts);
 }
 
 static void
@@ -633,7 +655,7 @@ static void
 ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
               const struct dp_packet *packet,
               const struct ofpact ofpacts[], size_t ofpacts_len,
-              struct ovs_list *next_ct_states, struct ds *output)
+              struct ovs_list *next_cts, struct ds *output)
 {
     struct ovs_list recirc_queue = OVS_LIST_INITIALIZER(&recirc_queue);
     ofproto_trace__(ofproto, flow, packet, &recirc_queue,
@@ -650,7 +672,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
             struct ct_dpif_info ct_info;
             memset(&ct_info, 0, sizeof(ct_info));
 
-            if (ovs_list_is_empty(next_ct_states)) {
+            if (ovs_list_is_empty(next_cts)) {
                 struct ds errs = DS_EMPTY_INITIALIZER;
                 struct ct_dpif_tuple tuple;
                 int err, indent_size;
@@ -681,10 +703,14 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
                 ds_put_format(output, " (use --ct-next to customize)");
                 ds_destroy(&errs);
             } else {
-                oftrace_pop_ct_state(next_ct_states, &ct_info.ct_state);
+                oftrace_pop_ct_info(next_cts, &ct_info);
                 ct_dpif_format_info(&ct_info, output);
             }
             recirc_node->flow.ct_state = ct_info.ct_state;
+            recirc_node->flow.ct_mark = ct_info.ct_mark;
+            if (ct_info.have_label) {
+                recirc_node->flow.ct_label = ct_info.ct_label;
+            }
         }
         ds_put_char(output, '\n');
         ds_put_char_multiple(output, '=', 79);
diff --git a/ofproto/ofproto-dpif-trace.h b/ofproto/ofproto-dpif-trace.h
index 5e51771b19b0..1c5ef645aa6d 100644
--- a/ofproto/ofproto-dpif-trace.h
+++ b/ofproto/ofproto-dpif-trace.h
@@ -28,6 +28,7 @@
  * each action (OFT_ACTION) executed in the table.
  */
 
+#include "ct-dpif.h"
 #include "openvswitch/compiler.h"
 #include "openvswitch/list.h"
 #include "flow.h"
@@ -72,10 +73,10 @@ struct oftrace_recirc_node {
     struct dp_packet *packet;
 };
 
-/* A node within a next_ct_states list. */
-struct oftrace_next_ct_state {
+/* A node within a next_cts list. */
+struct oftrace_next_ct {
     struct ovs_list node;       /* In next_ct_states. */
-    uint32_t state;
+    struct ct_dpif_info info;
 };
 
 void ofproto_dpif_trace_init(void);
diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man
index abc9481f0faf..c966beba1e57 100644
--- a/ofproto/ofproto-unixctl.man
+++ b/ofproto/ofproto-unixctl.man
@@ -51,12 +51,16 @@ wildcards.)  \fIbridge\fR names of the bridge through which
 .RS
 \fBofproto/trace\fR supports the following options:
 .
-.IP "--ct-next \fIflags\fR"
+.IP "--ct-next \fIfield\fR=\fIvalue\fR[,\fIfield\fR=\fIvalue\fR]"
 When the traced flow triggers conntrack actions, \fBofproto/trace\fR
 will automatically trace the forked packet processing pipeline with
-user specified ct_state.  This option sets the ct_state flags that the
-conntrack module will report. The \fIflags\fR must be a vertical
-bar separated list of the following connection tracking flags:
+user specified conntrack fields. Currently, the supported conntrack
+fields include ct_state, ct_mark, and ct_label.
+.
+.IP
+\fBct_state\fR:  Sets the ct_state flags that the conntrack module will
+report. The \fIvalue\fR of ct_state must be a vertical bar separated
+list of the following connection tracking flags:
 .
 .RS
 .IP \(bu
@@ -89,7 +93,9 @@ changed.
 .
 .IP
 When --ct-next is unspecified, or when there are fewer --ct-next options than
-ct actions, the \fIflags\fR default to trk|new.
+ct actions, ofproto/trace queries the conntrack fields from the datapath. If
+the conntrack fields can not be queried, then the \fIct_state\fR default to
+trk|new, and the \fIct_mark\fR and \fIct_label\fR default to zero.
 .
 .RE
 .
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 7353cbd6289a..32b837e5ea75 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -9834,14 +9834,14 @@ table=1,priority=1,action=drop
 dnl
 dnl Table 2
 dnl
-table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+new,tcp,action=ct(commit,zone=1),ct(table=3,zone=2)
-table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+est,tcp,action=ct(table=3,zone=2)
+table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+new,tcp,action=ct(commit,zone=1,exec(set_field:0x11->ct_mark)),ct(table=3,zone=2)
+table=2,priority=10,in_port=1,tcp,ct_zone=1,ct_state=+trk+est,ct_mark=0x11,tcp,action=ct(table=3,zone=2)
 table=2,priority=1,action=drop
 dnl
 dnl Table 3
 dnl
-table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+new,tcp,action=ct(commit,zone=2),4
-table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+est,tcp,action=3
+table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+new,tcp,action=ct(commit,zone=2,exec(set_field:0x22->ct_mark),exec(set_field:0x112233445566778899->ct_label)),4
+table=3,priority=10,in_port=1,tcp,ct_zone=2,ct_state=+trk+est,ct_mark=0x22,ct_label=0x112233445566778899,tcp,action=3
 table=2,priority=1,action=drop
 ])
 
@@ -9859,10 +9859,10 @@ AT_CHECK([tail -1 stdout], [0],
 
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,tcp'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
-  [Datapath actions: ct(commit,zone=2),4
+  [Datapath actions: ct(commit,zone=2,mark=0x22/0xffffffff,label=0x112233445566778899),4
 ])
 
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,tcp' --ct-next 'trk|est' --ct-next 'trk|est' ], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,tcp' --ct-next 'ct_state=trk|est,ct_mark=0x11' --ct-next 'ct_state=trk|est,ct_mark=0x22,ct_label=0x112233445566778899'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 3
 ])
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index 727f97f7a7dd..e9ebcd3e2688 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -4013,8 +4013,8 @@ 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=1,tcp,ct_state=+trk+new,action=ct(commit,zone=1,exec(set_field:0x77->ct_mark),exec(set_field:0x11223344556677889900->ct_label)),2
+table=1,priority=10,in_port=1,tcp,ct_state=+trk+est,ct_mark=0x77,ct_label=0x11223344556677889900,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
 ])
@@ -4029,7 +4029,7 @@ 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
+  [Datapath actions: ct(commit,zone=1,mark=0x77/0xffffffff,label=0x11223344556677889900),3
 ])
 AT_CHECK([grep -c 'ct_state=new|trk' stdout], [0], [2
 ])
-- 
2.7.4



More information about the dev mailing list