Example #1
0
/*
 * debug attribute TCA_NETEM_LOSS
 */
void debug_tca_netem_loss(int lev, struct rtattr *netem, const char *name)
{
    struct rtattr *loss[__NETEM_LOSS_MAX];

    rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(netem->rta_len));

    parse_nested_rtattr(loss, NETEM_LOSS_MAX, netem);

    if(loss[NETEM_LOSS_GI])
        debug_netem_loss_gi(lev+1, loss[NETEM_LOSS_GI],
            "NETEM_LOSS_GI");

    if(loss[NETEM_LOSS_GE])
        debug_netem_loss_ge(lev+1, loss[NETEM_LOSS_GE],
            "NETEM_LOSS_GE");
}
Example #2
0
/*
 * debug attribute TCA_EMATCH_*
 */
void debug_tca_ematch(int lev, struct rtattr *tca, const char *name)
{
    struct rtattr *em_tree[__TCA_EMATCH_TREE_MAX];
    int num = -1;

    rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(tca->rta_len));

    parse_nested_rtattr(em_tree, TCA_EMATCH_TREE_MAX, tca);

    if(em_tree[TCA_EMATCH_TREE_HDR])
        num = debug_tca_ematch_tree_hdr(lev+1, em_tree[TCA_EMATCH_TREE_HDR],
            "TCA_EMATCH_TREE_HDR");

    if(em_tree[TCA_EMATCH_TREE_LIST])
        debug_tca_ematch_tree_list(lev+1, em_tree[TCA_EMATCH_TREE_LIST],
            "TCA_EMATCH_TREE_LIST", num);
}
Example #3
0
/*
 * parse attribute TCA_EMATCH_*
 */
int parse_tca_ematch(char *msg, char *mp, struct rtattr *tca)
{
    struct rtattr *em_tree[__TCA_EMATCH_TREE_MAX];
    int num = -1;

    parse_nested_rtattr(em_tree, TCA_EMATCH_TREE_MAX, tca);

    if(em_tree[TCA_EMATCH_TREE_HDR])
        if(parse_tca_ematch_tree_hdr(em_tree[TCA_EMATCH_TREE_HDR], &num))
            return(1);

    if(em_tree[TCA_EMATCH_TREE_LIST])
        if(parse_tca_ematch_tree_list(msg, mp, em_tree[TCA_EMATCH_TREE_LIST], num))
            return(1);

    return(0);
}
Example #4
0
/*
 * debug attributes IFLA_BRPORT_*
 */
void debug_ifla_brport(int lev, struct rtattr *ifla)
{
    struct rtattr *brp[__IFLA_BRPORT_MAX];

	parse_nested_rtattr(brp, IFLA_BRPORT_MAX, ifla);

    if(brp[IFLA_BRPORT_STATE])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_STATE],
            "IFLA_BRPORT_STATE", conv_br_state);

    if(brp[IFLA_BRPORT_PRIORITY])
        debug_rta_u16(lev+1, brp[IFLA_BRPORT_PRIORITY],
            "IFLA_BRPORT_PRIORITY", NULL);

    if(brp[IFLA_BRPORT_COST])
        debug_rta_u32(lev+1, brp[IFLA_BRPORT_COST],
            "IFLA_BRPORT_COST", NULL);

    if(brp[IFLA_BRPORT_MODE])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_MODE],
            "IFLA_BRPORT_MODE", NULL);

    if(brp[IFLA_BRPORT_GUARD])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_GUARD],
            "IFLA_BRPORT_GUARD", NULL);

    if(brp[IFLA_BRPORT_PROTECT])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_PROTECT],
            "IFLA_BRPORT_PROTECT", NULL);

    if(brp[IFLA_BRPORT_FAST_LEAVE])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_FAST_LEAVE],
            "IFLA_BRPORT_FAST_LEAVE", NULL);

#if HAVE_DECL_IFLA_BRPORT_LEARNING
    if(brp[IFLA_BRPORT_LEARNING])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_LEARNING],
            "IFLA_BRPORT_LEARNING", NULL);

    if(brp[IFLA_BRPORT_UNICAST_FLOOD])
        debug_rta_u8(lev+1, brp[IFLA_BRPORT_UNICAST_FLOOD],
            "IFLA_BRPORT_UNICAST_FLOOD", NULL);
#endif

    return;
}
Example #5
0
/*
 * parse red options
 */
int parse_tca_options_red(char *msg, char **mp, struct rtattr *tca)
{
    struct rtattr *red[__TCA_RED_MAX];

    parse_nested_rtattr(red, TCA_RED_MAX, tca);

    if(red[TCA_RED_PARMS]) {
        struct tc_red_qopt *qopt;
        char limit[MAX_STR_SIZE] = "";
        char min[MAX_STR_SIZE] = "";
        char max[MAX_STR_SIZE] = "";
        char list[MAX_STR_SIZE] = "";

        if(RTA_PAYLOAD(red[TCA_RED_PARMS]) < sizeof(*qopt)) {
            rec_log("error: %s: TCA_RED_PARMS: payload too short", __func__);
            return(1);
        }
        qopt = (struct tc_red_qopt *)RTA_DATA(red[TCA_RED_PARMS]);

        conv_unit_size(limit, sizeof(limit), qopt->limit);
        conv_unit_size(min, sizeof(min), qopt->qth_min);
        conv_unit_size(max, sizeof(max), qopt->qth_max);

        *mp = add_log(msg, *mp, "limit=%s min=%s max=%s ", limit, min, max);
        if(qopt->flags) {
            conv_tc_red_flags(qopt->flags, list, sizeof(list), 0);
            *mp = add_log(msg, *mp, "flag=%s ", list);
        }
    }

#if HAVE_DECL_TCA_RED_MAX_P
    if(red[TCA_RED_MAX_P]) {
        if(RTA_PAYLOAD(red[TCA_RED_MAX_P]) < sizeof(unsigned)) {
            rec_log("error: %s: TCA_RED_MAX_P: payload too short", __func__);
            return(1);
        }
        *mp = add_log(msg, *mp, "probability=%g(%%) ",
            *(unsigned *)RTA_DATA(red[TCA_RED_MAX_P]) / pow(2, 32) * 100);
    }
#endif

    return(0);
}
Example #6
0
/*
 * debug red options
 */
void debug_tca_options_red(int lev, struct rtattr *tca, const char *name)
{
    struct rtattr *red[__TCA_RED_MAX];

    rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(tca->rta_len));
    parse_nested_rtattr(red, TCA_RED_MAX, tca);

    if(red[TCA_RED_PARMS])
        debug_tca_red_parms(lev+1, red[TCA_RED_PARMS],
            "TCA_RED_PARMS");

    if(red[TCA_RED_STAB])
        debug_rta_ignore(lev+1, red[TCA_RED_STAB],
            "TCA_RED_STAB");

#if HAVE_DECL_TCA_RED_MAX_P
    if(red[TCA_RED_MAX_P])
        debug_rta_u32(lev+1, red[TCA_RED_MAX_P],
            "TCA_RED_MAX_P", NULL);
#endif
}
Example #7
0
/*
 * debug choke options
 */
void debug_tca_options_choke(int lev, struct rtattr *tca, const char *name)
{
    struct rtattr *choke[__TCA_CHOKE_MAX];

    rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(tca->rta_len));
    parse_nested_rtattr(choke, TCA_CHOKE_MAX, tca);

    if(choke[TCA_CHOKE_PARMS])
        debug_tca_choke_parms(lev+1, choke[TCA_CHOKE_PARMS],
            "TCA_CHOKE_PARMS");

    if(choke[TCA_CHOKE_STAB])
        debug_rta_ignore(lev+1, choke[TCA_CHOKE_STAB],
            "TCA_CHOKE_STAB");

#if HAVE_DECL_TCA_GRED_MAX_P
    if(choke[TCA_CHOKE_MAX_P])
        debug_rta_u32(lev+1, choke[TCA_CHOKE_MAX_P],
            "TCA_CHOKE_MAX_P", NULL);
#endif
}
Example #8
0
/*
 * parse rsvp options
 */
int parse_tca_options_rsvp(char *msg, char **mp, struct tcmsg *tcm, struct rtattr *tca)
{
    struct rtattr *rsvp[__TCA_RSVP_MAX];
    char *mp_tmp = *mp;

    parse_nested_rtattr(rsvp, TCA_RSVP_MAX, tca);

    if(rsvp[TCA_RSVP_CLASSID])
        if(parse_tca_classid(msg, mp, rsvp[TCA_RSVP_CLASSID]))
            return(1);

    if(rsvp[TCA_RSVP_DST])
        if(parse_tca_rsvp_dst(msg, mp, tcm, rsvp[TCA_RSVP_DST]))
            return(1);

    if(rsvp[TCA_RSVP_SRC])
        if(parse_tca_rsvp_src(msg, mp, tcm, rsvp[TCA_RSVP_SRC]))
            return(1);

    if(rsvp[TCA_RSVP_PINFO])
        if(parse_tca_rsvp_pinfo(msg, mp, rsvp[TCA_RSVP_PINFO]))
            return(1);

    if(*mp != mp_tmp)
        rec_log("%s", msg);

    /* rollback pointer */
    *mp = mp_tmp;

    /* logging for each attribute below */
    if(rsvp[TCA_RSVP_POLICE])
        if(parse_tca_act_options_police(msg, *mp, rsvp[TCA_RSVP_POLICE]))
            return(1);

    if(rsvp[TCA_RSVP_ACT])
        if(parse_tca_acts(msg, *mp, rsvp[TCA_RSVP_ACT]))
            return(1);

    return(0);
}
Example #9
0
/*
 * debug attributes TCA_ACT_*
 */
void debug_tca_act(int lev, struct rtattr *acts)
{
    struct rtattr *act[__TCA_ACT_MAX];
    char kind[IFNAMSIZ];

    parse_nested_rtattr(act, __TCA_ACT_MAX-1, acts);

    if(act[TCA_ACT_KIND])
        debug_rta_str(lev+1, act[TCA_ACT_KIND], 
            "TCA_ACT_KIND", kind, sizeof(kind));

    if(act[TCA_ACT_OPTIONS])
        debug_tca_act_options(lev+1, act[TCA_ACT_OPTIONS],
            "TCA_ACT_OPTIONS", kind, sizeof(kind));

    if(act[TCA_ACT_INDEX])
        debug_rta_s32(lev+1, act[TCA_ACT_INDEX],
            "TCA_ACT_INDEX", NULL);

    if(act[TCA_ACT_STATS])
        debug_tca_act_stats(lev+1, act[TCA_ACT_STATS],
            "TCA_ACT_STATS");
}
Example #10
0
/*
 * parse choke options
 */
int parse_tca_options_choke(char *msg, char **mp, struct rtattr *tca)
{
    struct rtattr *choke[__TCA_CHOKE_MAX];

    parse_nested_rtattr(choke, TCA_CHOKE_MAX, tca);

    if(choke[TCA_CHOKE_PARMS]) {
        struct tc_choke_qopt *qopt;
        char list[MAX_STR_SIZE] = "";

        if(RTA_PAYLOAD(choke[TCA_CHOKE_PARMS]) < sizeof(*qopt)) {
            rec_log("error: %s: TCA_CHOKE_PARMS: payload too short", __func__);
            return(1);
        }
        qopt = (struct tc_choke_qopt *)RTA_DATA(choke[TCA_CHOKE_PARMS]);

        *mp = add_log(msg, *mp, "limit=%u(packet) min=%u(packet) max=%u(packet) ",
            qopt->limit, qopt->qth_min, qopt->qth_max);
        if(qopt->flags) {
            conv_tc_red_flags(qopt->flags, list, sizeof(list), 0);
            *mp = add_log(msg, *mp, "flag=%s ", list);
        }
    }

#if HAVE_DECL_TCA_CHOKE_MAX_P
    if(choke[TCA_CHOKE_MAX_P]) {
        if(RTA_PAYLOAD(choke[TCA_CHOKE_MAX_P]) < sizeof(unsigned)) {
            rec_log("error: %s: TCA_CHOKE_MAX_P: payload too short", __func__);
            return(1);
        }
        *mp = add_log(msg, *mp, "probability=%g(%%) ",
            *(unsigned *)RTA_DATA(choke[TCA_CHOKE_MAX_P]) / pow(2, 32) * 100);
    }
#endif

    return(0);
}
Example #11
0
/*
 * debug attribute TCA_EMATCH_TREE_LIST
 */
void debug_tca_ematch_tree_list(int lev, struct rtattr *em_tree, const char *name, int num)
{
    struct rtattr *em_list[num+1];
    struct tcf_ematch_hdr *hdr;
    int i;

    rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(em_tree->rta_len));

    parse_nested_rtattr(em_list, num, em_tree);

    /* no exist em_list[0] */
    for(i = 1; i < num + 1; i++) {
        if(!em_list[i])
            return;

        if(RTA_PAYLOAD(em_list[i]) < sizeof(*hdr)) {
            rec_dbg(lev+1, "[ tcf_ematch_hdr[%d](%d) ] -- payload too short --",
                i, sizeof(*hdr));
            return;
        }
        hdr = (struct tcf_ematch_hdr *)RTA_DATA(em_list[i]);

        rec_dbg(lev+1, "[ tcf_ematch_hdr[%d](%d) ]", i, sizeof(*hdr));
        rec_dbg(lev+1, "    matchid(%d): %hu", sizeof(hdr->matchid), hdr->matchid);
        rec_dbg(lev+1, "    kind(%d): %hu(%s)",
            sizeof(hdr->kind), hdr->kind, conv_tcf_em_kind(hdr->kind, 1));
        rec_dbg(lev+1, "    flags(%d): %hu(%s)",
            sizeof(hdr->flags), hdr->flags, conv_tcf_em_flag(hdr->flags, 1));
        rec_dbg(lev+1, "    pad(%d): %hu", sizeof(hdr->pad), hdr->pad);

        /* use (char*)hdr in order to count by one byte */
        switch(hdr->kind) {
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_CMP_H
            case TCF_EM_CMP:
                debug_ematch_cmp(lev+1, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr));
                break;
#endif
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_NBYTE_H
            case TCF_EM_NBYTE:
                debug_ematch_nbyte(lev+1, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr));
                break;
#endif
            case TCF_EM_U32:
                debug_ematch_u32(lev+1, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr));
                break;
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_META_H
            case TCF_EM_META:
                debug_ematch_meta(lev+1, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr));
                break;
#endif
            /* not implemented yet
            case TCF_EM_TEXT:
            case TCF_EM_VLAN:
            case TCF_EM_CANID:
            case TCF_EM_IPSET:
            */
            default:
                break;
        }
    }
}
Example #12
0
/*
 * parse attribute TCA_EMATCH_TREE_LIST
 */
int parse_tca_ematch_tree_list(char *msg, char *mp, struct rtattr *em_tree, int num)
{
    struct rtattr *em_list[num+1];
    struct tcf_ematch_hdr *hdr;
    int i;
    char *mp_tmp = mp;

    parse_nested_rtattr(em_list, num, em_tree);

    /* no exist em_list[0] */
    for(i = 1; i < num + 1; i++, mp = mp_tmp) {
        if(!em_list[i])
            return(0);

        if(RTA_PAYLOAD(em_list[i]) < sizeof(*hdr)) {
            rec_log("error: %s: payload too short", __func__);
            return(1);
        }
        hdr = (struct tcf_ematch_hdr *)RTA_DATA(em_list[i]);
        mp = add_log(msg, mp, "ematch=%s ", conv_tcf_em_kind(hdr->kind, 0));

        /* use (char*)hdr in order to count by one byte */
        switch(hdr->kind) {
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_CMP_H
            case TCF_EM_CMP:
                if(parse_ematch_cmp(msg, mp, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr)))
                    return(1);
                break;
#endif
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_NBYTE_H
            case TCF_EM_NBYTE:
                if(parse_ematch_nbyte(msg, mp, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr)))
                    return(1);
                break;
#endif
            case TCF_EM_U32:
                if(parse_ematch_u32(msg, mp, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr)))
                    return(1);
                break;
#ifdef HAVE_LINUX_TC_EMATCH_TC_EM_META_H
            case TCF_EM_META:
                if(parse_ematch_meta(msg, mp, (char *)hdr + sizeof(*hdr),
                    RTA_PAYLOAD(em_list[i]) - sizeof(*hdr)))
                    return(1);
                break;
#endif
            /* not implemented yet
            case TCF_EM_TEXT:
            case TCF_EM_VLAN:
            case TCF_EM_CANID:
            case TCF_EM_IPSET:
            */
            default:
                rec_log("%s", msg);
        }
    }

    return(0);
}
Example #13
0
/*
 * parse cbq options
 */
int parse_tca_options_cbq(char *msg, char **mp, struct rtattr *tca)
{
    struct rtattr *cbq[__TCA_CBQ_MAX];
    struct tc_ratespec *rspec = NULL;
    struct tc_cbq_lssopt *lss = NULL;
    struct tc_cbq_wrropt *wrr = NULL;
    struct tc_cbq_fopt *fopt = NULL;
    struct tc_cbq_ovl *ovl = NULL;

    parse_nested_rtattr(cbq, TCA_CBQ_MAX, tca);

    if(cbq[TCA_CBQ_LSSOPT]) {
        if(RTA_PAYLOAD(cbq[TCA_CBQ_LSSOPT]) < sizeof(*lss)) {
            rec_log("error: %s: TCA_CBQ_LSSOPT: payload too short", __func__);
            return(1);
        }
        lss = (struct tc_cbq_lssopt *)RTA_DATA(cbq[TCA_CBQ_LSSOPT]);
    }

    if(cbq[TCA_CBQ_WRROPT]) {
        if(RTA_PAYLOAD(cbq[TCA_CBQ_WRROPT]) < sizeof(*wrr)) {
            rec_log("error: %s: TCA_CBQ_WRROPT: payload too short", __func__);
            return(1);
        }
        wrr = (struct tc_cbq_wrropt *)RTA_DATA(cbq[TCA_CBQ_WRROPT]);
    }

    if(cbq[TCA_CBQ_FOPT]) {
        if(RTA_PAYLOAD(cbq[TCA_CBQ_FOPT]) < sizeof(*fopt)) {
            rec_log("error: %s: TCA_CBQ_FOPT: payload too short", __func__);
            return(1);
        }
        fopt = (struct tc_cbq_fopt *)RTA_DATA(cbq[TCA_CBQ_FOPT]);
    }

    if(cbq[TCA_CBQ_OVL_STRATEGY]) {
        if(RTA_PAYLOAD(cbq[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl)) {
            rec_log("error: %s: TCA_CBQ_OVL_STRATEGY: payload too short", __func__);
            return(1);
        }
        ovl = (struct tc_cbq_ovl *)RTA_DATA(cbq[TCA_CBQ_OVL_STRATEGY]);
    }

    if(cbq[TCA_CBQ_RATE]) {
        if(RTA_PAYLOAD(cbq[TCA_CBQ_RATE]) < sizeof(*rspec)) {
            rec_log("error: %s: TCA_CBQ_RATE: payload too short", __func__);
            return(1);
        }
        rspec = (struct tc_ratespec *)RTA_DATA(cbq[TCA_CBQ_RATE]);
    }

    if(rspec) {
        char rate[MAX_STR_SIZE];

        conv_unit_rate(rate, sizeof(rate), rspec->rate);
        *mp = add_log(msg, *mp, "rate=%s ", rate);
    }

    if(lss) {
        char maxidle[MAX_STR_SIZE];
        char minidle[MAX_STR_SIZE];

        get_us2tick();
        conv_unit_usec(maxidle, sizeof(maxidle),
                       (lss->maxidle >> lss->ewma_log) / us2tick);
        *mp = add_log(msg, *mp, "maxidle=%s ", maxidle);

        if(lss->minidle != 0x7fffffff) {
            conv_unit_usec(minidle, sizeof(minidle),
                           (lss->minidle >> lss->ewma_log) / us2tick);
            *mp = add_log(msg, *mp, "minidle=%s ", minidle);
        }

        *mp = add_log(msg, *mp, "level=%u avpkt=%u(byte) ", lss->level, lss->avpkt);
    }
Example #14
0
/*
 * parse flow options
 */
int parse_tca_options_flow(char *msg, char **mp, struct rtattr *tca)
{
    struct rtattr *flow[__TCA_FLOW_MAX];
    char *mp_tmp = *mp;

    parse_nested_rtattr(flow, TCA_FLOW_MAX, tca);

    if(flow[TCA_FLOW_BASECLASS])
        if(parse_tca_classid(msg, mp, flow[TCA_FLOW_BASECLASS]))
            return(1);

    if(flow[TCA_FLOW_KEYS])
        if(parse_tca_flow_keys(msg, mp, flow[TCA_FLOW_KEYS]))
            return(1);

    if(flow[TCA_FLOW_MODE])
        if(parse_tca_flow_mode(msg, mp, flow[TCA_FLOW_MODE]))
            return(1);

    if(flow[TCA_FLOW_MASK])
        if(parse_tca_mask(msg, mp, flow[TCA_FLOW_MASK]))
            return(1);

    if(flow[TCA_FLOW_XOR])
        if(parse_tca_flow_xor(msg, mp, flow[TCA_FLOW_XOR]))
            return(1);

    if(flow[TCA_FLOW_RSHIFT])
        if(parse_tca_flow_rshift(msg, mp, flow[TCA_FLOW_RSHIFT]))
            return(1);

    if(flow[TCA_FLOW_ADDEND])
        if(parse_tca_flow_addend(msg, mp, flow[TCA_FLOW_ADDEND]))
            return(1);

    if(flow[TCA_FLOW_DIVISOR])
        if(parse_tca_flow_divisor(msg, mp, flow[TCA_FLOW_DIVISOR]))
            return(1);

    if(flow[TCA_FLOW_PERTURB])
        if(parse_tca_flow_perturb(msg, mp, flow[TCA_FLOW_PERTURB]))
            return(1);

    if(*mp != mp_tmp)
        rec_log("%s", msg);

    /* rollback pointer */
    *mp = mp_tmp;

    /* logging for each attribute below */
    if(flow[TCA_FLOW_EMATCHES])
        if(parse_tca_ematch(msg, *mp, flow[TCA_FLOW_EMATCHES]))
            return(1);

    if(flow[TCA_FLOW_POLICE])
        if(parse_tca_act_options_police(msg, *mp, flow[TCA_FLOW_POLICE]))
            return(1);

    if(flow[TCA_FLOW_ACT])
        if(parse_tca_acts(msg, *mp, flow[TCA_FLOW_ACT]))
            return(1);

    return(0);
}
Example #15
0
/*
 * parse netem options
 */
int parse_tca_options_netem(char *msg, char **mp, struct rtattr *tca)
{
    struct rtattr *netem[__TCA_NETEM_MAX];
    struct tc_netem_qopt *qopt;
    struct tc_netem_corr *corr = NULL;
    struct tc_netem_reorder *reorder = NULL;
    struct tc_netem_corrupt *corrupt = NULL;
    const double max_percent_value = 0xffffffff;

    if(RTA_PAYLOAD(tca) < sizeof(*qopt)) {
        rec_log("error: %s: TCA_OPTIONS: payload too short", __func__);
        return(1);
    }
    qopt = (struct tc_netem_qopt *)RTA_DATA(tca);

    parse_rtattr(netem, TCA_NETEM_MAX,
        RTA_DATA(tca) + sizeof(struct tc_netem_qopt),
        RTA_PAYLOAD(tca) - sizeof(struct tc_netem_qopt));

    if(netem[TCA_NETEM_CORR]) {
        if(RTA_PAYLOAD(netem[TCA_NETEM_CORR]) < sizeof(*corr)) {
            rec_log("error: %s: TCA_NETEM_CORR: payload too short", __func__);
            return(1);
        }
        corr = (struct tc_netem_corr *)RTA_DATA(netem[TCA_NETEM_CORR]);
    }

    if(netem[TCA_NETEM_REORDER]) {
        if(RTA_PAYLOAD(netem[TCA_NETEM_REORDER]) < sizeof(*reorder)) {
            rec_log("error: %s: TCA_NETEM_REORDER: payload too short", __func__);
            return(1);
        }
        reorder = (struct tc_netem_reorder *)RTA_DATA(netem[TCA_NETEM_REORDER]);
    }

    if(netem[TCA_NETEM_CORRUPT]) {
        if(RTA_PAYLOAD(netem[TCA_NETEM_REORDER]) < sizeof(*corrupt)) {
            rec_log("error: %s: TCA_NETEM_REORDER: payload too short", __func__);
            return(1);
        }
        corrupt = (struct tc_netem_corrupt *)RTA_DATA(netem[TCA_NETEM_CORRUPT]);
    }

#if HAVE_DECL_TCA_NETEM_LOSS
    struct rtattr *loss[__NETEM_LOSS_MAX];
    struct tc_netem_gimodel *gimodel = NULL;
    struct tc_netem_gemodel *gemodel = NULL;

    if(netem[TCA_NETEM_LOSS]) {
        parse_nested_rtattr(loss, NETEM_LOSS_MAX, netem[TCA_NETEM_LOSS]);

        if(loss[NETEM_LOSS_GI]) {
            if(RTA_PAYLOAD(loss[NETEM_LOSS_GI]) < sizeof(*gimodel)) {
                rec_log("error: %s: NETEM_LOSS_GI: payload too short",
                    __func__);
                return(1);
            }
            gimodel = (struct tc_netem_gimodel *)RTA_DATA(loss[NETEM_LOSS_GI]);
        }

        if(loss[NETEM_LOSS_GE]) {
            if(RTA_PAYLOAD(loss[NETEM_LOSS_GE]) < sizeof(*gemodel)) {
                rec_log("error: %s: NETEM_LOSS_GE: payload too short",
                    __func__);
                return(1);
            }
            gemodel = (struct tc_netem_gemodel *)RTA_DATA(loss[NETEM_LOSS_GE]);
        }
    }
#endif

#if HAVE_DECL_TCA_NETEM_RATE
    struct tc_netem_rate *rate = NULL;

    if(netem[TCA_NETEM_RATE]) {

        if(RTA_PAYLOAD(netem[TCA_NETEM_RATE]) < sizeof(*rate)) {
            rec_log("error: %s: TCA_NETEM_RATE: payload too short", __func__);
            return(1);
        }
        rate = (struct tc_netem_rate *)RTA_DATA(netem[TCA_NETEM_RATE]);
    }
#endif

    if(qopt->limit)
        *mp = add_log(msg, *mp, "limit=%u(packet) ", qopt->limit);

    if(qopt->latency) {
        char latency[MAX_STR_SIZE];

        get_us2tick();
        conv_unit_usec(latency, sizeof(latency), qopt->latency / us2tick);
        *mp = add_log(msg, *mp, "delay=%s ", latency);
        if(corr && corr->delay_corr)
            *mp = add_log(msg, *mp, "delay-correlation=%g(%%) ",
                (double)corr->delay_corr / max_percent_value * 100.);
    }

    if(qopt->jitter) {
        char jitter[MAX_STR_SIZE];

        get_us2tick();
        conv_unit_usec(jitter, sizeof(jitter), qopt->jitter / us2tick);
        *mp = add_log(msg, *mp, "jitter=%s ", jitter);
    }

    if(qopt->loss) {
        *mp = add_log(msg, *mp, "loss=%g(%%) ",
            (double)qopt->loss / max_percent_value * 100.);
        if(corr && corr->loss_corr)
            *mp = add_log(msg, *mp, "loss-correlation=%g(%%) ",
                (double)corr->loss_corr / max_percent_value * 100.);
    }

#if HAVE_DECL_TCA_NETEM_LOSS
    if(gimodel)
        *mp = add_log(msg, *mp, "loss-state(p13/p31/p32/p23/p14)="
            "%g(%%)/%g(%%)/%g(%%)/%g(%%)/%g(%%) ",
            (double)gimodel->p13 / max_percent_value * 100.,
            (double)gimodel->p31 / max_percent_value * 100.,
            (double)gimodel->p32 / max_percent_value * 100.,
            (double)gimodel->p23 / max_percent_value * 100.,
            (double)gimodel->p14 / max_percent_value * 100.);

    if(gemodel)
        *mp = add_log(msg, *mp, "loss-gemodel(p/r/1-h/1-k)="
            "%g(%%)/%g(%%)/%g(%%)/%g(%%) ",
            (double)gemodel->p / max_percent_value * 100.,
            (double)gemodel->r / max_percent_value * 100.,
            (double)gemodel->h / max_percent_value * 100.,
            (double)gemodel->k1 / max_percent_value * 100.);
#endif

    if(qopt->duplicate) {
        *mp = add_log(msg, *mp, "duplicate=%g(%%) ",
            (double)qopt->duplicate / max_percent_value * 100.);
        if(corr && corr->dup_corr)
            *mp = add_log(msg, *mp, "duplicate-correlation=%g(%%) ",
                (double)corr->dup_corr / max_percent_value * 100.);
    }

    if(reorder && reorder->probability) {
        *mp = add_log(msg, *mp, "reorder=%g(%%) ",
            (double)reorder->probability / max_percent_value * 100.);
        if(reorder->correlation)
            *mp = add_log(msg, *mp, "reorder-correlation=%g(%%) ",
                (double)reorder->correlation / max_percent_value * 100.);
    }

    if(corrupt && corrupt->probability) {
        *mp = add_log(msg, *mp, "corrupt=%g(%%) ",
            (double)corrupt->probability / max_percent_value * 100.);
        if(corrupt->correlation)
            *mp = add_log(msg, *mp, "corrupt-correlation=%g(%%) ",
                (double)corrupt->correlation / max_percent_value * 100.);
    }

#if HAVE_DECL_TCA_NETEM_RATE
    if(rate && rate->rate) {
        char netem_rate[MAX_STR_SIZE];

        conv_unit_rate(netem_rate, sizeof(netem_rate), rate->rate);
        *mp = add_log(msg, *mp, "rate=%s ", netem_rate);
                if(rate->packet_overhead)
            *mp = add_log(msg, *mp, "packet-overhead=%u(byte) ", rate->packet_overhead);

                if(rate->cell_size)
            *mp = add_log(msg, *mp, "cell-size=%u(byte) ", rate->cell_size);

                if(rate->cell_overhead)
            *mp = add_log(msg, *mp, "cell-overhead=%u(byte) ", rate->cell_overhead);
    }
#endif

    if(qopt->gap)
        *mp = add_log(msg, *mp, "gap=%u(packet) ", qopt->gap);

#if HAVE_DECL_TCA_NETEM_ECN
    if(netem[TCA_NETEM_ECN] && *(unsigned *)RTA_DATA(netem[TCA_NETEM_ECN]))
        *mp = add_log(msg, *mp, "ecn=on ");
#endif

    return(0);
}