示例#1
0
文件: process.c 项目: InCNTRE/OFTT
char *
process_escape_args(char **argv)
{
    struct ds ds = DS_EMPTY_INITIALIZER;
    char **argp;
    for (argp = argv; *argp; argp++) {
        const char *arg = *argp;
        const char *p;
        if (argp != argv) {
            ds_put_char(&ds, ' ');
        }
        if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) {
            ds_put_char(&ds, '"');
            for (p = arg; *p; p++) {
                if (*p == '\\' || *p == '\"') {
                    ds_put_char(&ds, '\\');
                }
                ds_put_char(&ds, *p);
            }
            ds_put_char(&ds, '"');
        } else {
            ds_put_cstr(&ds, arg);
        }
    }
    return ds_cstr(&ds);
}
示例#2
0
action_syntax_error(struct action_context *ctx, const char *message, ...)
{
    if (action_error_handle_common(ctx)) {
        return;
    }

    struct ds s;

    ds_init(&s);
    ds_put_cstr(&s, "Syntax error");
    if (ctx->lexer->token.type == LEX_T_END) {
        ds_put_cstr(&s, " at end of input");
    } else if (ctx->lexer->start) {
        ds_put_format(&s, " at `%.*s'",
                      (int) (ctx->lexer->input - ctx->lexer->start),
                      ctx->lexer->start);
    }

    if (message) {
        ds_put_char(&s, ' ');

        va_list args;
        va_start(args, message);
        ds_put_format_valist(&s, message, args);
        va_end(args);
    }
    ds_put_char(&s, '.');

    ctx->error = ds_steal_cstr(&s);
}
示例#3
0
文件: flow.c 项目: leihnwong/lazyctrl
void
flow_format(struct ds *ds, const struct flow *flow)
{
    ds_put_format(ds, "priority:%"PRIu32
                      ",tunnel:%#"PRIx64
                      ",metadata:%#"PRIx64
                      ",in_port:%04"PRIx16,
                      flow->skb_priority,
                      ntohll(flow->tun_id),
                      ntohll(flow->metadata),
                      flow->in_port);

    ds_put_format(ds, ",tci(");
    if (flow->vlan_tci) {
        ds_put_format(ds, "vlan:%"PRIu16",pcp:%d",
                      vlan_tci_to_vid(flow->vlan_tci),
                      vlan_tci_to_pcp(flow->vlan_tci));
    } else {
        ds_put_char(ds, '0');
    }
    ds_put_format(ds, ") mac("ETH_ADDR_FMT"->"ETH_ADDR_FMT
                      ") type:%04"PRIx16,
                  ETH_ADDR_ARGS(flow->dl_src),
                  ETH_ADDR_ARGS(flow->dl_dst),
                  ntohs(flow->dl_type));

    if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
        ds_put_format(ds, " label:%#"PRIx32" proto:%"PRIu8" tos:%#"PRIx8
                          " ttl:%"PRIu8" ipv6(",
                      ntohl(flow->ipv6_label), flow->nw_proto,
                      flow->nw_tos, flow->nw_ttl);
        print_ipv6_addr(ds, &flow->ipv6_src);
        ds_put_cstr(ds, "->");
        print_ipv6_addr(ds, &flow->ipv6_dst);
        ds_put_char(ds, ')');
    } else {
        ds_put_format(ds, " proto:%"PRIu8" tos:%#"PRIx8" ttl:%"PRIu8
                          " ip("IP_FMT"->"IP_FMT")",
                          flow->nw_proto, flow->nw_tos, flow->nw_ttl,
                          IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst));
    }
    if (flow->nw_frag) {
        ds_put_format(ds, " frag(%s)",
                      flow->nw_frag == FLOW_NW_FRAG_ANY ? "first"
                      : flow->nw_frag == (FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER)
                      ? "later" : "<error>");
    }
    if (flow->tp_src || flow->tp_dst) {
        ds_put_format(ds, " port(%"PRIu16"->%"PRIu16")",
                ntohs(flow->tp_src), ntohs(flow->tp_dst));
    }
    if (!eth_addr_is_zero(flow->arp_sha) || !eth_addr_is_zero(flow->arp_tha)) {
        ds_put_format(ds, " arp_ha("ETH_ADDR_FMT"->"ETH_ADDR_FMT")",
                ETH_ADDR_ARGS(flow->arp_sha),
                ETH_ADDR_ARGS(flow->arp_tha));
    }
}
示例#4
0
/* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */
void
ds_put_utf8(struct ds *ds, int uc)
{
    if (uc <= 0x7f) {
        ds_put_char(ds, uc);
    } else if (uc <= 0x7ff) {
        ds_put_char(ds, 0xc0 | (uc >> 6));
        ds_put_char(ds, 0x80 | (uc & 0x3f));
    } else if (uc <= 0xffff) {
示例#5
0
enum ofperr
ofputil_queue_get_config_reply_format(struct ds *string,
                                      const struct ofp_header *oh,
                                      const struct ofputil_port_map *port_map)
{
    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));

    struct ofputil_queue_config *queues = NULL;
    size_t allocated_queues = 0;
    size_t n = 0;

    int retval = 0;
    for (;;) {
        if (n >= allocated_queues) {
            queues = x2nrealloc(queues, &allocated_queues, sizeof *queues);
        }
        retval = ofputil_pull_queue_get_config_reply(&b, &queues[n]);
        if (retval) {
            break;
        }
        n++;
    }

    qsort(queues, n, sizeof *queues, compare_queues);

    ds_put_char(string, ' ');

    ofp_port_t port = 0;
    for (const struct ofputil_queue_config *q = queues; q < &queues[n]; q++) {
        if (q->port != port) {
            port = q->port;

            ds_put_cstr(string, "port=");
            ofputil_format_port(port, port_map, string);
            ds_put_char(string, '\n');
        }

        ds_put_format(string, "queue %"PRIu32":", q->queue);
        print_queue_rate(string, "min_rate", q->min_rate);
        print_queue_rate(string, "max_rate", q->max_rate);
        ds_put_char(string, '\n');
    }

    ds_chomp(string, ' ');
    free(queues);

    return retval != EOF ? retval : 0;
}
示例#6
0
/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
 * describes. */
void
multipath_format(const struct ofpact_multipath *mp, struct ds *s)
{
    const char *fields, *algorithm;

    fields = flow_hash_fields_to_str(mp->fields);

    switch (mp->algorithm) {
    case NX_MP_ALG_MODULO_N:
        algorithm = "modulo_n";
        break;
    case NX_MP_ALG_HASH_THRESHOLD:
        algorithm = "hash_threshold";
        break;
    case NX_MP_ALG_HRW:
        algorithm = "hrw";
        break;
    case NX_MP_ALG_ITER_HASH:
        algorithm = "iter_hash";
        break;
    default:
        algorithm = "<unknown>";
    }

    ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
                  fields, mp->basis, algorithm, mp->max_link + 1,
                  mp->arg);
    mf_format_subfield(&mp->dst, s);
    ds_put_char(s, ')');
}
示例#7
0
static void
test_lex(const char *input)
{
    struct ds output;

    ds_init(&output);
    struct lexer lexer;

    lexer_init(&lexer, input);
    ds_clear(&output);
    while (lexer_get(&lexer) != LEX_T_END) {
        size_t len = output.length;
        lex_token_format(&lexer.token, &output);

        /* Check that the formatted version can really be parsed back
         * losslessly. */
        if (lexer.token.type != LEX_T_ERROR) {
            const char *s = ds_cstr(&output) + len;
            struct lexer l2;

            lexer_init(&l2, s);
            lexer_get(&l2);
            compare_token(&lexer.token, &l2.token);
            lexer_destroy(&l2);
        }
        ds_put_char(&output, ' ');
    }
    lexer_destroy(&lexer);

    ds_chomp(&output, ' ');
    puts(ds_cstr(&output));
    ds_destroy(&output);
}
示例#8
0
文件: match.c 项目: AlickHill/Lantern
static void
format_flow_tunnel(struct ds *s, const struct match *match)
{
    const struct flow_wildcards *wc = &match->wc;
    const struct flow_tnl *tnl = &match->flow.tunnel;

    switch (wc->masks.tunnel.tun_id) {
    case 0:
        break;
    case CONSTANT_HTONLL(UINT64_MAX):
        ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(tnl->tun_id));
        break;
    default:
        ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
                      ntohll(tnl->tun_id),
                      ntohll(wc->masks.tunnel.tun_id));
        break;
    }
    format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
    format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);

    if (wc->masks.tunnel.ip_tos) {
        ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
    }
    if (wc->masks.tunnel.ip_ttl) {
        ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl);
    }
    if (wc->masks.tunnel.flags) {
        format_flags(s, flow_tun_flag_to_string, tnl->flags, '|');
        ds_put_char(s, ',');
    }
}
示例#9
0
void
ofputil_format_bundle_add(struct ds *s,
                          const struct ofputil_bundle_add_msg *badd,
                          const struct ofputil_port_map *port_map,
                          const struct ofputil_table_map *table_map,
                          int verbosity)
{
    ds_put_char(s, '\n');
    ds_put_format(s, " bundle_id=%#"PRIx32, badd->bundle_id);
    ds_put_cstr(s, " flags=");
    ofp_print_bit_names(s, badd->flags, bundle_flags_to_name, ' ');

    ds_put_char(s, '\n');
    char *msg = ofp_to_string(badd->msg, ntohs(badd->msg->length), port_map,
                              table_map, verbosity);
    ds_put_and_free_cstr(s, msg);
}
示例#10
0
static void
format_generic_odp_action(struct ds *ds, const struct nlattr *a)
{
    size_t len = nl_attr_get_size(a);

    ds_put_format(ds, "action%"PRId16, nl_attr_type(a));
    if (len) {
        const uint8_t *unspec;
        unsigned int i;

        unspec = nl_attr_get(a);
        for (i = 0; i < len; i++) {
            ds_put_char(ds, i ? ' ': '(');
            ds_put_format(ds, "%02x", unspec[i]);
        }
        ds_put_char(ds, ')');
    }
}
示例#11
0
文件: match.c 项目: AlickHill/Lantern
static void
format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6],
                  const uint8_t mask[6])
{
    if (!eth_addr_is_zero(mask)) {
        ds_put_format(s, "%s=", name);
        eth_format_masked(eth, mask, s);
        ds_put_char(s, ',');
    }
}
示例#12
0
文件: match.c 项目: AlickHill/Lantern
static void
format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
                  ovs_be32 netmask)
{
    if (netmask) {
        ds_put_format(s, "%s=", name);
        ip_format_masked(ip, netmask, s);
        ds_put_char(s, ',');
    }
}
示例#13
0
/* Breaks 'words' into words at white space, respecting shell-like quoting
 * conventions, and appends the words to 'svec'. */
void
svec_parse_words(struct svec *svec, const char *words)
{
    struct ds word = DS_EMPTY_INITIALIZER;
    const char *p, *q;

    for (p = words; *p != '\0'; p = q) {
        int quote = 0;

        while (isspace((unsigned char) *p)) {
            p++;
        }
        if (*p == '\0') {
            break;
        }

        ds_clear(&word);
        for (q = p; *q != '\0'; q++) {
            if (*q == quote) {
                quote = 0;
            } else if (*q == '\'' || *q == '"') {
                quote = *q;
            } else if (*q == '\\' && (!quote || quote == '"')) {
                q++;
                if (*q == '\0') {
                    VLOG_WARN(LOG_MODULE, "%s: ends in trailing backslash", words);
                    break;
                }
                ds_put_char(&word, *q);
            } else if (isspace((unsigned char) *q) && !quote) {
                q++;
                break;
            } else {
                ds_put_char(&word, *q);
            }
        }
        svec_add(svec, ds_cstr(&word));
        if (quote) {
            VLOG_WARN(LOG_MODULE, "%s: word ends inside quoted string", words);
        }
    }
    ds_destroy(&word);
}
示例#14
0
文件: match.c 项目: AlickHill/Lantern
static void
format_ipv6_netmask(struct ds *s, const char *name,
                    const struct in6_addr *addr,
                    const struct in6_addr *netmask)
{
    if (!ipv6_mask_is_any(netmask)) {
        ds_put_format(s, "%s=", name);
        print_ipv6_masked(s, addr, netmask);
        ds_put_char(s, ',');
    }
}
示例#15
0
enum ofperr
ofputil_queue_stats_reply_format(struct ds *string,
                                 const struct ofp_header *oh,
                                 const struct ofputil_port_map *port_map,
                                 int verbosity)
{
    ds_put_format(string, " %"PRIuSIZE" queues\n",
                  ofputil_count_queue_stats(oh));
    if (verbosity < 1) {
        return 0;
    }

    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
    for (;;) {
        struct ofputil_queue_stats qs;
        int retval;

        retval = ofputil_decode_queue_stats(&qs, &b);
        if (retval) {
            return retval != EOF ? retval : 0;
        }

        ds_put_cstr(string, "  port ");
        ofputil_format_port(qs.port_no, port_map, string);
        ds_put_cstr(string, " queue ");
        ofp_print_queue_name(string, qs.queue_id);
        ds_put_cstr(string, ": ");

        print_queue_stat(string, "bytes=", qs.tx_bytes, 1);
        print_queue_stat(string, "pkts=", qs.tx_packets, 1);
        print_queue_stat(string, "errors=", qs.tx_errors, 1);

        ds_put_cstr(string, "duration=");
        if (qs.duration_sec != UINT32_MAX) {
            ofp_print_duration(string, qs.duration_sec, qs.duration_nsec);
        } else {
            ds_put_char(string, '?');
        }
        ds_put_char(string, '\n');
    }
}
示例#16
0
static void
format_uint32_masked(struct ds *s, const char *name,
                   uint32_t value, uint32_t mask)
{
    if (mask) {
        ds_put_format(s, "%s=%#"PRIx32, name, value);
        if (mask != UINT32_MAX) {
            ds_put_format(s, "/%#"PRIx32, mask);
        }
        ds_put_char(s, ',');
    }
}
示例#17
0
static void
format_be64_masked(struct ds *s, const char *name,
                   ovs_be64 value, ovs_be64 mask)
{
    if (mask != htonll(0)) {
        ds_put_format(s, "%s=%#"PRIx64, name, ntohll(value));
        if (mask != OVS_BE64_MAX) {
            ds_put_format(s, "/%#"PRIx64, ntohll(mask));
        }
        ds_put_char(s, ',');
    }
}
示例#18
0
文件: bundle.c 项目: ALutzG/ovs
/* Appends a human-readable representation of 'nab' to 's'. */
void
bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
{
    const char *action, *fields, *algorithm;
    size_t i;

    fields = flow_hash_fields_to_str(bundle->fields);

    switch (bundle->algorithm) {
    case NX_BD_ALG_HRW:
        algorithm = "hrw";
        break;
    case NX_BD_ALG_ACTIVE_BACKUP:
        algorithm = "active_backup";
        break;
    default:
        algorithm = "<unknown>";
    }

    action = bundle->dst.field ? "bundle_load" : "bundle";

    ds_put_format(s, "%s%s(%s%s,%"PRIu16",%s,%s,", colors.paren, action,
                  colors.end, fields, bundle->basis, algorithm, "ofport");

    if (bundle->dst.field) {
        mf_format_subfield(&bundle->dst, s);
        ds_put_char(s, ',');
    }

    ds_put_format(s, "%sslaves:%s", colors.param, colors.end);
    for (i = 0; i < bundle->n_slaves; i++) {
        if (i) {
            ds_put_char(s, ',');
        }

        ofputil_format_port(bundle->slaves[i], s);
    }

    ds_put_format(s, "%s)%s", colors.paren, colors.end);
}
示例#19
0
/* Appends a string representation of the TCP flags value 'tcp_flags'
 * (e.g. obtained via packet_get_tcp_flags() or TCP_FLAGS) to 's', in the
 * format used by tcpdump. */
void
packet_format_tcp_flags(struct ds *s, uint8_t tcp_flags)
{
    if (!tcp_flags) {
        ds_put_cstr(s, "none");
        return;
    }

    if (tcp_flags & TCP_SYN) {
        ds_put_char(s, 'S');
    }
    if (tcp_flags & TCP_FIN) {
        ds_put_char(s, 'F');
    }
    if (tcp_flags & TCP_PSH) {
        ds_put_char(s, 'P');
    }
    if (tcp_flags & TCP_RST) {
        ds_put_char(s, 'R');
    }
    if (tcp_flags & TCP_URG) {
        ds_put_char(s, 'U');
    }
    if (tcp_flags & TCP_ACK) {
        ds_put_char(s, '.');
    }
    if (tcp_flags & 0x40) {
        ds_put_cstr(s, "[40]");
    }
    if (tcp_flags & 0x80) {
        ds_put_cstr(s, "[80]");
    }
}
void
ds_put_printable(struct ds *ds, const char *s, size_t n) 
{
    ds_reserve(ds, ds->length + n);
    while (n-- > 0) {
        unsigned char c = *s++;
        if (c < 0x20 || c > 0x7e || c == '\\' || c == '"') {
            ds_put_format(ds, "\\%03o", (int) c);
        } else {
            ds_put_char(ds, c);
        }
    }
}
void
print_ipv6_masked(struct ds *s, const struct in6_addr *addr,
                  const struct in6_addr *mask)
{
    print_ipv6_addr(s, addr);
    if (mask && !ipv6_mask_is_exact(mask)) {
        if (ipv6_is_cidr(mask)) {
            int cidr_bits = ipv6_count_cidr_bits(mask);
            ds_put_format(s, "/%d", cidr_bits);
        } else {
            ds_put_char(s, '/');
            print_ipv6_addr(s, mask);
        }
    }
}
示例#22
0
文件: odp-util.c 项目: InCNTRE/OFTT
void
format_odp_actions(struct ds *ds, const union odp_action *actions,
                   size_t n_actions)
{
    size_t i;
    for (i = 0; i < n_actions; i++) {
        if (i) {
            ds_put_char(ds, ',');
        }
        format_odp_action(ds, &actions[i]);
    }
    if (!n_actions) {
        ds_put_cstr(ds, "drop");
    }
}
示例#23
0
static void
format_be32_masked(struct ds *s, const char *name,
                   ovs_be32 value, ovs_be32 mask)
{
    if (mask != htonl(0)) {
        ds_put_format(s, "%s=", name);
        if (mask == OVS_BE32_MAX) {
            ds_put_format(s, "%"PRIu32, ntohl(value));
        } else {
            ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
                          ntohl(value), ntohl(mask));
        }
        ds_put_char(s, ',');
    }
}
示例#24
0
文件: match.c 项目: AlickHill/Lantern
static void
format_be16_masked(struct ds *s, const char *name,
                   ovs_be16 value, ovs_be16 mask)
{
    if (mask != htons(0)) {
        ds_put_format(s, "%s=", name);
        if (mask == htons(UINT16_MAX)) {
            ds_put_format(s, "%"PRIu16, ntohs(value));
        } else {
            ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16,
                          ntohs(value), ntohs(mask));
        }
        ds_put_char(s, ',');
    }
}
示例#25
0
void
ofputil_switch_features_format(struct ds *s,
                               const struct ofputil_switch_features *features)
{
    ds_put_format(s, " dpid:%016"PRIx64"\n", features->datapath_id);

    ds_put_format(s, "n_tables:%"PRIu8", n_buffers:%"PRIu32,
                  features->n_tables, features->n_buffers);
    if (features->auxiliary_id) {
        ds_put_format(s, ", auxiliary_id:%"PRIu8, features->auxiliary_id);
    }
    ds_put_char(s, '\n');

    ds_put_cstr(s, "capabilities: ");
    ofp_print_bit_names(s, features->capabilities,
                        ofputil_capabilities_to_name, ' ');
    ds_put_char(s, '\n');

    if (features->ofpacts) {
        ds_put_cstr(s, "actions: ");
        ofpact_bitmap_format(features->ofpacts, s);
        ds_put_char(s, '\n');
    }
}
示例#26
0
static void
print_queue_stat(struct ds *string, const char *leader, uint64_t stat,
                 int more)
{
    ds_put_cstr(string, leader);
    if (stat != UINT64_MAX) {
        ds_put_format(string, "%"PRIu64, stat);
    } else {
        ds_put_char(string, '?');
    }
    if (more) {
        ds_put_cstr(string, ", ");
    } else {
        ds_put_cstr(string, "\n");
    }
}
示例#27
0
文件: lex.c 项目: l8huang/ovs
static void
lex_token_format_masked_integer(const struct lex_token *token, struct ds *s)
{
    enum lex_format format = lex_token_get_format(token);

    lex_token_format_value(&token->value, format, s);
    ds_put_char(s, '/');

    const union mf_subvalue *mask = &token->mask;
    if (format == LEX_F_IPV4 && ip_is_cidr(mask->ipv4)) {
        ds_put_format(s, "%d", ip_count_cidr_bits(mask->ipv4));
    } else if (token->format == LEX_F_IPV6 && ipv6_is_cidr(&mask->ipv6)) {
        ds_put_format(s, "%d", ipv6_count_cidr_bits(&mask->ipv6));
    } else {
        lex_token_format_value(&token->mask, format, s);
    }
}
示例#28
0
/* Returns a string that represents 'protocols'.  The return value might be a
 * comma-separated list if 'protocols' doesn't have a simple name.  The return
 * value is "none" if 'protocols' is 0.
 *
 * The caller must free the returned string (with free()). */
char *
ofputil_protocols_to_string(enum ofputil_protocol protocols)
{
    struct ds s;

    ovs_assert(!(protocols & ~OFPUTIL_P_ANY));
    if (protocols == 0) {
        return xstrdup("none");
    }

    ds_init(&s);
    while (protocols) {
        const struct proto_abbrev *p;
        int i;

        if (s.length) {
            ds_put_char(&s, ',');
        }

        for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
            if ((protocols & p->protocol) == p->protocol) {
                ds_put_cstr(&s, p->name);
                protocols &= ~p->protocol;
                goto match;
            }
        }

        for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
            enum ofputil_protocol bit = 1u << i;

            if (protocols & bit) {
                ds_put_cstr(&s, ofputil_protocol_to_string(bit));
                protocols &= ~bit;
                goto match;
            }
        }
        OVS_NOT_REACHED();

    match: ;
    }
    return ds_steal_cstr(&s);
}
log_wakeup(const struct backtrace *backtrace, const char *format, ...)
{
    struct ds ds;
    va_list args;

    ds_init(&ds);
    va_start(args, format);
    ds_put_format_valist(&ds, format, args);
    va_end(args);

    if (backtrace) {
        int i;

        ds_put_char(&ds, ':');
        for (i = 0; i < backtrace->n_frames; i++) {
            ds_put_format(&ds, " 0x%x", backtrace->frames[i]);
        }
    }
    VLOG_DBG("%s", ds_cstr(&ds));
    ds_destroy(&ds);
}
示例#30
0
static void
format_flow_tunnel(struct ds *s, const struct match *match)
{
    const struct flow_wildcards *wc = &match->wc;
    const struct flow_tnl *tnl = &match->flow.tunnel;

    format_be64_masked(s, "tun_id", tnl->tun_id, wc->masks.tunnel.tun_id);
    format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
    format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);

    if (wc->masks.tunnel.ip_tos) {
        ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
    }
    if (wc->masks.tunnel.ip_ttl) {
        ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl);
    }
    if (wc->masks.tunnel.flags) {
        format_flags(s, flow_tun_flag_to_string, tnl->flags, '|');
        ds_put_char(s, ',');
    }
}