+ struct simap port_names;
+ struct ofpbuf keybuf;
+ struct flow flow;
+ struct ofpbuf odp_actions;
+ struct hmap actions_per_flow;
+ struct actions_for_flow **afs;
+ struct actions_for_flow *af;
+ struct nlattr *a;
+ size_t n_afs;
+ struct ds s;
+ int left;
+ int i;
+
+ ds_init(&s);
+
+ simap_init(&port_names);
+ for (i = 3; i < argc; i++) {
+ char name[16];
+ int number;
+
+ if (ovs_scan(argv[i], "%15[^=]=%d", name, &number)) {
+ uintptr_t n = number;
+ simap_put(&port_names, name, n);
+ } else {
+ ovs_fatal(0, "%s: expected NAME=NUMBER", argv[i]);
+ }
+ }
+
+ /* Parse flow key. */
+ ofpbuf_init(&keybuf, 0);
+ run(odp_flow_from_string(argv[1], &port_names, &keybuf, NULL),
+ "odp_flow_key_from_string");
+
+ ds_clear(&s);
+ odp_flow_format(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf), NULL, 0, NULL, &s, verbosity);
+ printf("input flow: %s\n", ds_cstr(&s));
+
+ run(odp_flow_key_to_flow(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf), &flow),
+ "odp_flow_key_to_flow");
+ ofpbuf_uninit(&keybuf);
+
+ /* Parse actions. */
+ ofpbuf_init(&odp_actions, 0);
+ run(odp_actions_from_string(argv[2], &port_names, &odp_actions),
+ "odp_actions_from_string");
+ simap_destroy(&port_names);
+
+ if (verbosity) {
+ ds_clear(&s);
+ format_odp_actions(&s, ofpbuf_data(&odp_actions), ofpbuf_size(&odp_actions));
+ printf("input actions: %s\n", ds_cstr(&s));
+ }
+
+ hmap_init(&actions_per_flow);
+ NL_ATTR_FOR_EACH (a, left, ofpbuf_data(&odp_actions), ofpbuf_size(&odp_actions)) {
+ const struct ovs_action_push_vlan *push;
+ switch(nl_attr_type(a)) {
+ case OVS_ACTION_ATTR_POP_VLAN:
+ flow.vlan_tci = htons(0);
+ continue;
+
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ push = nl_attr_get_unspec(a, sizeof *push);
+ flow.vlan_tci = push->vlan_tci;
+ continue;
+ }
+
+ af = get_actions_for_flow(&actions_per_flow, &flow);
+ nl_msg_put_unspec(&af->actions, nl_attr_type(a),
+ nl_attr_get(a), nl_attr_get_size(a));
+ }
+
+ n_afs = hmap_count(&actions_per_flow);
+ afs = xmalloc(n_afs * sizeof *afs);
+ i = 0;
+ HMAP_FOR_EACH (af, hmap_node, &actions_per_flow) {
+ afs[i++] = af;
+ }
+ ovs_assert(i == n_afs);
+
+ qsort(afs, n_afs, sizeof *afs, compare_actions_for_flow);
+
+ for (i = 0; i < n_afs; i++) {
+ const struct actions_for_flow *af = afs[i];
+
+ sort_output_actions(ofpbuf_data(&af->actions), ofpbuf_size(&af->actions));
+
+ if (af->flow.vlan_tci != htons(0)) {
+ printf("vlan(vid=%"PRIu16",pcp=%d): ",
+ vlan_tci_to_vid(af->flow.vlan_tci),
+ vlan_tci_to_pcp(af->flow.vlan_tci));
+ } else {
+ printf("no vlan: ");
+ }
+
+ if (eth_type_mpls(af->flow.dl_type)) {
+ printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
+ mpls_lse_to_label(af->flow.mpls_lse[0]),
+ mpls_lse_to_tc(af->flow.mpls_lse[0]),
+ mpls_lse_to_ttl(af->flow.mpls_lse[0]));
+ } else {
+ printf("no mpls: ");
+ }
+
+ ds_clear(&s);
+ format_odp_actions(&s, ofpbuf_data(&af->actions), ofpbuf_size(&af->actions));
+ puts(ds_cstr(&s));
+ }
+ ds_destroy(&s);