static int xfrm_state_extra_flag_parse(__u32 *extra_flags, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; int len = strlen(*argv); if (len > 2 && strncmp(*argv, "0x", 2) == 0) { __u32 val = 0; if (get_u32(&val, *argv, 16)) invarg("\"EXTRA-FLAG\" is invalid", *argv); *extra_flags = val; } else { while (1) { if (strcmp(*argv, "dont-encap-dscp") == 0) *extra_flags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP; else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } } *argcp = argc; *argvp = argv; return 0; }
static int xfrm_policy_flag_parse(__u8 *flags, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; int len = strlen(*argv); if (len > 2 && strncmp(*argv, "0x", 2) == 0) { __u8 val = 0; if (get_u8(&val, *argv, 16)) invarg("\"FLAG\" is invalid", *argv); *flags = val; } else { while (1) { if (strcmp(*argv, "localok") == 0) *flags |= XFRM_POLICY_LOCALOK; else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } } *argcp = argc; *argvp = argv; return 0; }
static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; char *idp = NULL; while (1) { if (strcmp(*argv, "mode") == 0) { NEXT_ARG(); xfrm_mode_parse(&tmpl->mode, &argc, &argv); } else if (strcmp(*argv, "reqid") == 0) { NEXT_ARG(); xfrm_reqid_parse(&tmpl->reqid, &argc, &argv); } else if (strcmp(*argv, "level") == 0) { NEXT_ARG(); if (strcmp(*argv, "required") == 0) tmpl->optional = 0; else if (strcmp(*argv, "use") == 0) tmpl->optional = 1; else invarg("\"LEVEL\" is invalid\n", *argv); } else { if (idp) { PREV_ARG(); /* back track */ break; } idp = *argv; xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family, 0, &argc, &argv); if (preferred_family == AF_UNSPEC) preferred_family = tmpl->family; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } if (argc == *argcp) missarg("TMPL"); *argcp = argc; *argvp = argv; return 0; }
static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; int len = strlen(*argv); if (len > 2 && strncmp(*argv, "0x", 2) == 0) { __u8 val = 0; if (get_u8(&val, *argv, 16)) invarg("FLAG value is invalid", *argv); *flags = val; } else { while (1) { if (strcmp(*argv, "noecn") == 0) *flags |= XFRM_STATE_NOECN; else if (strcmp(*argv, "decap-dscp") == 0) *flags |= XFRM_STATE_DECAP_DSCP; else if (strcmp(*argv, "nopmtudisc") == 0) *flags |= XFRM_STATE_NOPMTUDISC; else if (strcmp(*argv, "wildrecv") == 0) *flags |= XFRM_STATE_WILDRECV; else if (strcmp(*argv, "icmp") == 0) *flags |= XFRM_STATE_ICMP; else if (strcmp(*argv, "af-unspec") == 0) *flags |= XFRM_STATE_AF_UNSPEC; else if (strcmp(*argv, "align4") == 0) *flags |= XFRM_STATE_ALIGN4; else if (strcmp(*argv, "esn") == 0) *flags |= XFRM_STATE_ESN; else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } } *argcp = argc; *argvp = argv; return 0; }
static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; int len = strlen(*argv); if (len > 2 && strncmp(*argv, "0x", 2) == 0) { __u8 val = 0; if (get_u8(&val, *argv, 16)) invarg("\"FLAG\" is invalid", *argv); *flags = val; } else { while (1) { if (strcmp(*argv, "noecn") == 0) *flags |= XFRM_STATE_NOECN; else if (strcmp(*argv, "decap-dscp") == 0) *flags |= XFRM_STATE_DECAP_DSCP; else if (strcmp(*argv, "wildrecv") == 0) *flags |= XFRM_STATE_WILDRECV; else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } } filter.state_flags_mask = XFRM_FILTER_MASK_FULL; *argcp = argc; *argvp = argv; return 0; }
static int bond_parse_opt(struct link_util *lu, int argc, char **argv, struct nlmsghdr *n) { __u8 mode, use_carrier, primary_reselect, fail_over_mac; __u8 xmit_hash_policy, num_peer_notif, all_slaves_active; __u8 lacp_rate, ad_select, tlb_dynamic_lb; __u16 ad_user_port_key, ad_actor_sys_prio; __u32 miimon, updelay, downdelay, arp_interval, arp_validate; __u32 arp_all_targets, resend_igmp, min_links, lp_interval; __u32 packets_per_slave; unsigned ifindex; while (argc > 0) { if (matches(*argv, "mode") == 0) { NEXT_ARG(); if (get_index(mode_tbl, *argv) < 0) invarg("invalid mode", *argv); mode = get_index(mode_tbl, *argv); addattr8(n, 1024, IFLA_BOND_MODE, mode); } else if (matches(*argv, "active_slave") == 0) { NEXT_ARG(); ifindex = if_nametoindex(*argv); if (!ifindex) return -1; addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, ifindex); } else if (matches(*argv, "clear_active_slave") == 0) { addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, 0); } else if (matches(*argv, "miimon") == 0) { NEXT_ARG(); if (get_u32(&miimon, *argv, 0)) invarg("invalid miimon", *argv); addattr32(n, 1024, IFLA_BOND_MIIMON, miimon); } else if (matches(*argv, "updelay") == 0) { NEXT_ARG(); if (get_u32(&updelay, *argv, 0)) invarg("invalid updelay", *argv); addattr32(n, 1024, IFLA_BOND_UPDELAY, updelay); } else if (matches(*argv, "downdelay") == 0) { NEXT_ARG(); if (get_u32(&downdelay, *argv, 0)) invarg("invalid downdelay", *argv); addattr32(n, 1024, IFLA_BOND_DOWNDELAY, downdelay); } else if (matches(*argv, "use_carrier") == 0) { NEXT_ARG(); if (get_u8(&use_carrier, *argv, 0)) invarg("invalid use_carrier", *argv); addattr8(n, 1024, IFLA_BOND_USE_CARRIER, use_carrier); } else if (matches(*argv, "arp_interval") == 0) { NEXT_ARG(); if (get_u32(&arp_interval, *argv, 0)) invarg("invalid arp_interval", *argv); addattr32(n, 1024, IFLA_BOND_ARP_INTERVAL, arp_interval); } else if (matches(*argv, "arp_ip_target") == 0) { struct rtattr * nest = addattr_nest(n, 1024, IFLA_BOND_ARP_IP_TARGET); if (NEXT_ARG_OK()) { NEXT_ARG(); char *targets = strdupa(*argv); char *target = strtok(targets, ","); int i; for(i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) { __u32 addr = get_addr32(target); addattr32(n, 1024, i, addr); target = strtok(NULL, ","); } addattr_nest_end(n, nest); } addattr_nest_end(n, nest); } else if (matches(*argv, "arp_validate") == 0) { NEXT_ARG(); if (get_index(arp_validate_tbl, *argv) < 0) invarg("invalid arp_validate", *argv); arp_validate = get_index(arp_validate_tbl, *argv); addattr32(n, 1024, IFLA_BOND_ARP_VALIDATE, arp_validate); } else if (matches(*argv, "arp_all_targets") == 0) { NEXT_ARG(); if (get_index(arp_all_targets_tbl, *argv) < 0) invarg("invalid arp_all_targets", *argv); arp_all_targets = get_index(arp_all_targets_tbl, *argv); addattr32(n, 1024, IFLA_BOND_ARP_ALL_TARGETS, arp_all_targets); } else if (matches(*argv, "primary") == 0) { NEXT_ARG(); ifindex = if_nametoindex(*argv); if (!ifindex) return -1; addattr32(n, 1024, IFLA_BOND_PRIMARY, ifindex); } else if (matches(*argv, "primary_reselect") == 0) { NEXT_ARG(); if (get_index(primary_reselect_tbl, *argv) < 0) invarg("invalid primary_reselect", *argv); primary_reselect = get_index(primary_reselect_tbl, *argv); addattr8(n, 1024, IFLA_BOND_PRIMARY_RESELECT, primary_reselect); } else if (matches(*argv, "fail_over_mac") == 0) { NEXT_ARG(); if (get_index(fail_over_mac_tbl, *argv) < 0) invarg("invalid fail_over_mac", *argv); fail_over_mac = get_index(fail_over_mac_tbl, *argv); addattr8(n, 1024, IFLA_BOND_FAIL_OVER_MAC, fail_over_mac); } else if (matches(*argv, "xmit_hash_policy") == 0) { NEXT_ARG(); if (get_index(xmit_hash_policy_tbl, *argv) < 0) invarg("invalid xmit_hash_policy", *argv); xmit_hash_policy = get_index(xmit_hash_policy_tbl, *argv); addattr8(n, 1024, IFLA_BOND_XMIT_HASH_POLICY, xmit_hash_policy); } else if (matches(*argv, "resend_igmp") == 0) { NEXT_ARG(); if (get_u32(&resend_igmp, *argv, 0)) invarg("invalid resend_igmp", *argv); addattr32(n, 1024, IFLA_BOND_RESEND_IGMP, resend_igmp); } else if (matches(*argv, "num_grat_arp") == 0 || matches(*argv, "num_unsol_na") == 0) { NEXT_ARG(); if (get_u8(&num_peer_notif, *argv, 0)) invarg("invalid num_grat_arp|num_unsol_na", *argv); addattr8(n, 1024, IFLA_BOND_NUM_PEER_NOTIF, num_peer_notif); } else if (matches(*argv, "all_slaves_active") == 0) { NEXT_ARG(); if (get_u8(&all_slaves_active, *argv, 0)) invarg("invalid all_slaves_active", *argv); addattr8(n, 1024, IFLA_BOND_ALL_SLAVES_ACTIVE, all_slaves_active); } else if (matches(*argv, "min_links") == 0) { NEXT_ARG(); if (get_u32(&min_links, *argv, 0)) invarg("invalid min_links", *argv); addattr32(n, 1024, IFLA_BOND_MIN_LINKS, min_links); } else if (matches(*argv, "lp_interval") == 0) { NEXT_ARG(); if (get_u32(&lp_interval, *argv, 0)) invarg("invalid lp_interval", *argv); addattr32(n, 1024, IFLA_BOND_LP_INTERVAL, lp_interval); } else if (matches(*argv, "packets_per_slave") == 0) { NEXT_ARG(); if (get_u32(&packets_per_slave, *argv, 0)) invarg("invalid packets_per_slave", *argv); addattr32(n, 1024, IFLA_BOND_PACKETS_PER_SLAVE, packets_per_slave); } else if (matches(*argv, "lacp_rate") == 0) { NEXT_ARG(); if (get_index(lacp_rate_tbl, *argv) < 0) invarg("invalid lacp_rate", *argv); lacp_rate = get_index(lacp_rate_tbl, *argv); addattr8(n, 1024, IFLA_BOND_AD_LACP_RATE, lacp_rate); } else if (matches(*argv, "ad_select") == 0) { NEXT_ARG(); if (get_index(ad_select_tbl, *argv) < 0) invarg("invalid ad_select", *argv); ad_select = get_index(ad_select_tbl, *argv); addattr8(n, 1024, IFLA_BOND_AD_SELECT, ad_select); } else if (matches(*argv, "ad_user_port_key") == 0) { NEXT_ARG(); if (get_u16(&ad_user_port_key, *argv, 0)) invarg("invalid ad_user_port_key", *argv); addattr16(n, 1024, IFLA_BOND_AD_USER_PORT_KEY, ad_user_port_key); } else if (matches(*argv, "ad_actor_sys_prio") == 0) { NEXT_ARG(); if (get_u16(&ad_actor_sys_prio, *argv, 0)) invarg("invalid ad_actor_sys_prio", *argv); addattr16(n, 1024, IFLA_BOND_AD_ACTOR_SYS_PRIO, ad_actor_sys_prio); } else if (matches(*argv, "ad_actor_system") == 0) { int len; char abuf[32]; NEXT_ARG(); len = ll_addr_a2n(abuf, sizeof(abuf), *argv); if (len < 0) return -1; addattr_l(n, 1024, IFLA_BOND_AD_ACTOR_SYSTEM, abuf, len); } else if (matches(*argv, "tlb_dynamic_lb") == 0) { NEXT_ARG(); if (get_u8(&tlb_dynamic_lb, *argv, 0)) { invarg("invalid tlb_dynamic_lb", *argv); return -1; } addattr8(n, 1024, IFLA_BOND_TLB_DYNAMIC_LB, tlb_dynamic_lb); } else if (matches(*argv, "help") == 0) { explain(); return -1; } else { fprintf(stderr, "bond: unknown command \"%s\"?\n", *argv); explain(); return -1; } argc--, argv++; } return 0; }
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) { struct rtnl_handle rth; struct { struct nlmsghdr n; struct xfrm_usersa_info xsinfo; char buf[RTA_BUF_SIZE]; } req; struct xfrm_replay_state replay; char *idp = NULL; char *aeadop = NULL; char *ealgop = NULL; char *aalgop = NULL; char *calgop = NULL; char *coap = NULL; char *sctxp = NULL; struct xfrm_mark mark = {0, 0}; struct { struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; } ctx; memset(&req, 0, sizeof(req)); memset(&replay, 0, sizeof(replay)); memset(&ctx, 0, sizeof(ctx)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); req.n.nlmsg_flags = NLM_F_REQUEST|flags; req.n.nlmsg_type = cmd; req.xsinfo.family = preferred_family; req.xsinfo.lft.soft_byte_limit = XFRM_INF; req.xsinfo.lft.hard_byte_limit = XFRM_INF; req.xsinfo.lft.soft_packet_limit = XFRM_INF; req.xsinfo.lft.hard_packet_limit = XFRM_INF; while (argc > 0) { if (strcmp(*argv, "mode") == 0) { NEXT_ARG(); xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); } else if (strcmp(*argv, "mark") == 0) { xfrm_parse_mark(&mark, &argc, &argv); } else if (strcmp(*argv, "reqid") == 0) { NEXT_ARG(); xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); } else if (strcmp(*argv, "seq") == 0) { NEXT_ARG(); xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); } else if (strcmp(*argv, "replay-window") == 0) { NEXT_ARG(); if (get_u8(&req.xsinfo.replay_window, *argv, 0)) invarg("\"replay-window\" value is invalid", *argv); } else if (strcmp(*argv, "replay-seq") == 0) { NEXT_ARG(); if (get_u32(&replay.seq, *argv, 0)) invarg("\"replay-seq\" value is invalid", *argv); } else if (strcmp(*argv, "replay-oseq") == 0) { NEXT_ARG(); if (get_u32(&replay.oseq, *argv, 0)) invarg("\"replay-oseq\" value is invalid", *argv); } else if (strcmp(*argv, "flag") == 0) { NEXT_ARG(); xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); } else if (strcmp(*argv, "sel") == 0) { NEXT_ARG(); xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); } else if (strcmp(*argv, "limit") == 0) { NEXT_ARG(); xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); } else if (strcmp(*argv, "encap") == 0) { struct xfrm_encap_tmpl encap; inet_prefix oa; NEXT_ARG(); xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); NEXT_ARG(); if (get_u16(&encap.encap_sport, *argv, 0)) invarg("\"encap\" sport value is invalid", *argv); encap.encap_sport = htons(encap.encap_sport); NEXT_ARG(); if (get_u16(&encap.encap_dport, *argv, 0)) invarg("\"encap\" dport value is invalid", *argv); encap.encap_dport = htons(encap.encap_dport); NEXT_ARG(); get_addr(&oa, *argv, AF_UNSPEC); memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, (void *)&encap, sizeof(encap)); } else if (strcmp(*argv, "coa") == 0) { inet_prefix coa; xfrm_address_t xcoa; if (coap) duparg("coa", *argv); coap = *argv; NEXT_ARG(); get_prefix(&coa, *argv, preferred_family); if (coa.family == AF_UNSPEC) invarg("\"coa\" address family is AF_UNSPEC", *argv); if (coa.bytelen > sizeof(xcoa)) invarg("\"coa\" address length is too large", *argv); memset(&xcoa, 0, sizeof(xcoa)); memcpy(&xcoa, &coa.data, coa.bytelen); addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR, (void *)&xcoa, sizeof(xcoa)); } else if (strcmp(*argv, "ctx") == 0) { char *context; if (sctxp) duparg("ctx", *argv); sctxp = *argv; NEXT_ARG(); context = *argv; xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX, (void *)&ctx, ctx.sctx.len); } else { /* try to assume ALGO */ int type = xfrm_algotype_getbyname(*argv); switch (type) { case XFRMA_ALG_AEAD: case XFRMA_ALG_CRYPT: case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH_TRUNC: case XFRMA_ALG_COMP: { /* ALGO */ struct { union { struct xfrm_algo alg; struct xfrm_algo_aead aead; struct xfrm_algo_auth auth; } u; char buf[XFRM_ALGO_KEY_BUF_SIZE]; } alg = {}; int len; __u32 icvlen, trunclen; char *name; char *key; char *buf; switch (type) { case XFRMA_ALG_AEAD: if (aeadop) duparg("ALGO-TYPE", *argv); aeadop = *argv; break; case XFRMA_ALG_CRYPT: if (ealgop) duparg("ALGO-TYPE", *argv); ealgop = *argv; break; case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH_TRUNC: if (aalgop) duparg("ALGO-TYPE", *argv); aalgop = *argv; break; case XFRMA_ALG_COMP: if (calgop) duparg("ALGO-TYPE", *argv); calgop = *argv; break; default: /* not reached */ invarg("\"ALGO-TYPE\" is invalid\n", *argv); } if (!NEXT_ARG_OK()) missarg("ALGO-NAME"); NEXT_ARG(); name = *argv; if (!NEXT_ARG_OK()) missarg("ALGO-KEY"); NEXT_ARG(); key = *argv; buf = alg.u.alg.alg_key; len = sizeof(alg.u.alg); switch (type) { case XFRMA_ALG_AEAD: if (!NEXT_ARG_OK()) missarg("ALGO-ICV-LEN"); NEXT_ARG(); if (get_u32(&icvlen, *argv, 0)) invarg("\"aead\" ICV length is invalid", *argv); alg.u.aead.alg_icv_len = icvlen; buf = alg.u.aead.alg_key; len = sizeof(alg.u.aead); break; case XFRMA_ALG_AUTH_TRUNC: if (!NEXT_ARG_OK()) missarg("ALGO-TRUNC-LEN"); NEXT_ARG(); if (get_u32(&trunclen, *argv, 0)) invarg("\"auth\" trunc length is invalid", *argv); alg.u.auth.alg_trunc_len = trunclen; buf = alg.u.auth.alg_key; len = sizeof(alg.u.auth); break; } xfrm_algo_parse((void *)&alg, type, name, key, buf, sizeof(alg.buf)); len += alg.u.alg.alg_key_len; addattr_l(&req.n, sizeof(req.buf), type, (void *)&alg, len); break; } default: /* try to assume ID */ if (idp) invarg("unknown", *argv); idp = *argv; /* ID */ xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, &req.xsinfo.family, 0, &argc, &argv); if (preferred_family == AF_UNSPEC) preferred_family = req.xsinfo.family; } } argc--; argv++; } if (replay.seq || replay.oseq) addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL, (void *)&replay, sizeof(replay)); if (!idp) { fprintf(stderr, "Not enough information: \"ID\" is required\n"); exit(1); } if (mark.m & mark.v) { int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, (void *)&mark, sizeof(mark)); if (r < 0) { fprintf(stderr, "XFRMA_MARK failed\n"); exit(1); } } switch (req.xsinfo.mode) { case XFRM_MODE_TRANSPORT: case XFRM_MODE_TUNNEL: if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { fprintf(stderr, "\"mode\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } break; case XFRM_MODE_ROUTEOPTIMIZATION: case XFRM_MODE_IN_TRIGGER: if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { fprintf(stderr, "\"mode\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } if (req.xsinfo.id.spi != 0) { fprintf(stderr, "\"spi\" must be 0 with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } break; default: break; } if (aeadop || ealgop || aalgop || calgop) { if (!xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } else { if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit (1); } } if (coap) { if (!xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { fprintf(stderr, "\"coa\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } else { if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { fprintf(stderr, "\"coa\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit (1); } } if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) exit(1); if (req.xsinfo.family == AF_UNSPEC) req.xsinfo.family = AF_INET; if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) exit(2); rtnl_close(&rth); return 0; }
int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, int loose, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; inet_prefix dst; inet_prefix src; memset(&dst, 0, sizeof(dst)); memset(&src, 0, sizeof(src)); while (1) { if (strcmp(*argv, "src") == 0) { NEXT_ARG(); get_prefix(&src, *argv, preferred_family); if (src.family == AF_UNSPEC) invarg("\"src\" address family is AF_UNSPEC", *argv); if (family) *family = src.family; memcpy(saddr, &src.data, sizeof(*saddr)); filter.id_src_mask = src.bitlen; } else if (strcmp(*argv, "dst") == 0) { NEXT_ARG(); get_prefix(&dst, *argv, preferred_family); if (dst.family == AF_UNSPEC) invarg("\"dst\" address family is AF_UNSPEC", *argv); if (family) *family = dst.family; memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); filter.id_dst_mask = dst.bitlen; } else if (strcmp(*argv, "proto") == 0) { int ret; NEXT_ARG(); ret = xfrm_xfrmproto_getbyname(*argv); if (ret < 0) invarg("\"XFRM_PROTO\" is invalid", *argv); id->proto = (__u8)ret; filter.id_proto_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "spi") == 0) { __u32 spi; NEXT_ARG(); if (get_u32(&spi, *argv, 0)) invarg("\"SPI\" is invalid", *argv); spi = htonl(spi); id->spi = spi; filter.id_spi_mask = XFRM_FILTER_MASK_FULL; } else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } if (src.family && dst.family && (src.family != dst.family)) invarg("the same address family is required between \"src\" and \"dst\"", *argv); if (loose == 0 && id->proto == 0) missarg("XFRM_PROTO"); if (argc == *argcp) missarg("ID"); *argcp = argc; *argvp = argv; return 0; }
int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; inet_prefix dst; inet_prefix src; char *upspecp = NULL; memset(&dst, 0, sizeof(dst)); memset(&src, 0, sizeof(src)); while (1) { if (strcmp(*argv, "src") == 0) { NEXT_ARG(); get_prefix(&src, *argv, preferred_family); if (src.family == AF_UNSPEC) invarg("\"src\" address family is AF_UNSPEC", *argv); sel->family = src.family; memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); sel->prefixlen_s = src.bitlen; filter.sel_src_mask = src.bitlen; } else if (strcmp(*argv, "dst") == 0) { NEXT_ARG(); get_prefix(&dst, *argv, preferred_family); if (dst.family == AF_UNSPEC) invarg("\"dst\" address family is AF_UNSPEC", *argv); sel->family = dst.family; memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); sel->prefixlen_d = dst.bitlen; filter.sel_dst_mask = dst.bitlen; } else if (strcmp(*argv, "dev") == 0) { int ifindex; NEXT_ARG(); if (strcmp(*argv, "none") == 0) ifindex = 0; else { ifindex = ll_name_to_index(*argv); if (ifindex <= 0) invarg("\"DEV\" is invalid", *argv); } sel->ifindex = ifindex; filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; } else { if (upspecp) { PREV_ARG(); /* back track */ break; } else { upspecp = *argv; xfrm_selector_upspec_parse(sel, &argc, &argv); } } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } if (src.family && dst.family && (src.family != dst.family)) invarg("the same address family is required between \"src\" and \"dst\"", *argv); if (argc == *argcp) missarg("SELECTOR"); *argcp = argc; *argvp = argv; return 0; }
static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; char *sportp = NULL; char *dportp = NULL; char *typep = NULL; char *codep = NULL; while (1) { if (strcmp(*argv, "proto") == 0) { __u8 upspec; NEXT_ARG(); if (strcmp(*argv, "any") == 0) upspec = 0; else { struct protoent *pp; pp = getprotobyname(*argv); if (pp) upspec = pp->p_proto; else { if (get_u8(&upspec, *argv, 0)) invarg("\"PROTO\" is invalid", *argv); } } sel->proto = upspec; filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "sport") == 0) { sportp = *argv; NEXT_ARG(); if (get_u16(&sel->sport, *argv, 0)) invarg("\"PORT\" is invalid", *argv); sel->sport = htons(sel->sport); if (sel->sport) sel->sport_mask = ~((__u16)0); filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "dport") == 0) { dportp = *argv; NEXT_ARG(); if (get_u16(&sel->dport, *argv, 0)) invarg("\"PORT\" is invalid", *argv); sel->dport = htons(sel->dport); if (sel->dport) sel->dport_mask = ~((__u16)0); filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "type") == 0) { typep = *argv; NEXT_ARG(); if (get_u16(&sel->sport, *argv, 0) || (sel->sport & ~((__u16)0xff))) invarg("\"type\" value is invalid", *argv); sel->sport = htons(sel->sport); sel->sport_mask = ~((__u16)0); filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "code") == 0) { codep = *argv; NEXT_ARG(); if (get_u16(&sel->dport, *argv, 0) || (sel->dport & ~((__u16)0xff))) invarg("\"code\" value is invalid", *argv); sel->dport = htons(sel->dport); sel->dport_mask = ~((__u16)0); filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; } else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } if (argc == *argcp) missarg("UPSPEC"); if (sportp || dportp) { switch (sel->proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_SCTP: case IPPROTO_DCCP: break; default: fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto)); exit(1); } } if (typep || codep) { switch (sel->proto) { case IPPROTO_ICMP: case IPPROTO_ICMPV6: case IPPROTO_MH: break; default: fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto)); exit(1); } } *argcp = argc; *argvp = argv; return 0; }
int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, int loose, int *argcp, char ***argvp) { int argc = *argcp; char **argv = *argvp; inet_prefix dst; inet_prefix src; memset(&dst, 0, sizeof(dst)); memset(&src, 0, sizeof(src)); while (1) { if (strcmp(*argv, "src") == 0) { NEXT_ARG(); get_prefix(&src, *argv, preferred_family); if (src.family == AF_UNSPEC) invarg("value after \"src\" has an unrecognized address family", *argv); if (family) *family = src.family; memcpy(saddr, &src.data, sizeof(*saddr)); filter.id_src_mask = src.bitlen; } else if (strcmp(*argv, "dst") == 0) { NEXT_ARG(); get_prefix(&dst, *argv, preferred_family); if (dst.family == AF_UNSPEC) invarg("value after \"dst\" has an unrecognized address family", *argv); if (family) *family = dst.family; memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); filter.id_dst_mask = dst.bitlen; } else if (strcmp(*argv, "proto") == 0) { int ret; NEXT_ARG(); ret = xfrm_xfrmproto_getbyname(*argv); if (ret < 0) invarg("XFRM-PROTO value is invalid", *argv); id->proto = (__u8)ret; filter.id_proto_mask = XFRM_FILTER_MASK_FULL; } else if (strcmp(*argv, "spi") == 0) { __u32 spi; NEXT_ARG(); if (get_u32(&spi, *argv, 0)) invarg("SPI value is invalid", *argv); spi = htonl(spi); id->spi = spi; filter.id_spi_mask = XFRM_FILTER_MASK_FULL; } else { PREV_ARG(); /* back track */ break; } if (!NEXT_ARG_OK()) break; NEXT_ARG(); } if (src.family && dst.family && (src.family != dst.family)) invarg("the same address family is required between values after \"src\" and \"dst\"", *argv); if (id->spi && id->proto) { if (xfrm_xfrmproto_is_ro(id->proto)) { fprintf(stderr, "\"spi\" is invalid with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(id->proto)); exit(1); } else if (id->proto == IPPROTO_COMP && ntohl(id->spi) >= 0x10000) { fprintf(stderr, "SPI value is too large with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(id->proto)); exit(1); } } if (loose == 0 && id->proto == 0) missarg("XFRM-PROTO"); if (argc == *argcp) missarg("ID"); *argcp = argc; *argvp = argv; return 0; }
static int iplink_parse_vf(int vf, int *argcp, char ***argvp, struct iplink_req *req, int dev_index) { char new_rate_api = 0, count = 0, override_legacy_rate = 0; struct ifla_vf_rate tivt; int len, argc = *argcp; char **argv = *argvp; struct rtattr *vfinfo; tivt.min_tx_rate = -1; tivt.max_tx_rate = -1; vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO); while (NEXT_ARG_OK()) { NEXT_ARG(); count++; if (!matches(*argv, "max_tx_rate")) { /* new API in use */ new_rate_api = 1; /* override legacy rate */ override_legacy_rate = 1; } else if (!matches(*argv, "min_tx_rate")) { /* new API in use */ new_rate_api = 1; } } while (count--) { /* rewind arg */ PREV_ARG(); } while (NEXT_ARG_OK()) { NEXT_ARG(); if (matches(*argv, "mac") == 0) { struct ifla_vf_mac ivm; NEXT_ARG(); ivm.vf = vf; len = ll_addr_a2n((char *)ivm.mac, 32, *argv); if (len < 0) return -1; addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm)); } else if (matches(*argv, "vlan") == 0) { struct ifla_vf_vlan ivv; NEXT_ARG(); if (get_unsigned(&ivv.vlan, *argv, 0)) { invarg("Invalid \"vlan\" value\n", *argv); } ivv.vf = vf; ivv.qos = 0; if (NEXT_ARG_OK()) { NEXT_ARG(); if (matches(*argv, "qos") == 0) { NEXT_ARG(); if (get_unsigned(&ivv.qos, *argv, 0)) { invarg("Invalid \"qos\" value\n", *argv); } } else { /* rewind arg */ PREV_ARG(); } } addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv)); } else if (matches(*argv, "rate") == 0) { struct ifla_vf_tx_rate ivt; NEXT_ARG(); if (get_unsigned(&ivt.rate, *argv, 0)) { invarg("Invalid \"rate\" value\n", *argv); } ivt.vf = vf; if (!new_rate_api) addattr_l(&req->n, sizeof(*req), IFLA_VF_TX_RATE, &ivt, sizeof(ivt)); else if (!override_legacy_rate) tivt.max_tx_rate = ivt.rate; } else if (matches(*argv, "max_tx_rate") == 0) { NEXT_ARG(); if (get_unsigned(&tivt.max_tx_rate, *argv, 0)) invarg("Invalid \"max tx rate\" value\n", *argv); tivt.vf = vf; } else if (matches(*argv, "min_tx_rate") == 0) { NEXT_ARG(); if (get_unsigned(&tivt.min_tx_rate, *argv, 0)) invarg("Invalid \"min tx rate\" value\n", *argv); tivt.vf = vf; } else if (matches(*argv, "spoofchk") == 0) { struct ifla_vf_spoofchk ivs; NEXT_ARG(); if (matches(*argv, "on") == 0) ivs.setting = 1; else if (matches(*argv, "off") == 0) ivs.setting = 0; else invarg("Invalid \"spoofchk\" value\n", *argv); ivs.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); } else if (matches(*argv, "state") == 0) { struct ifla_vf_link_state ivl; NEXT_ARG(); if (matches(*argv, "auto") == 0) ivl.link_state = IFLA_VF_LINK_STATE_AUTO; else if (matches(*argv, "enable") == 0) ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; else if (matches(*argv, "disable") == 0) ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; else invarg("Invalid \"state\" value\n", *argv); ivl.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl)); } else { /* rewind arg */ PREV_ARG(); break; } } if (new_rate_api) { int tmin, tmax; if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) { ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev_index); if (tivt.min_tx_rate == -1) tivt.min_tx_rate = tmin; if (tivt.max_tx_rate == -1) tivt.max_tx_rate = tmax; } addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt, sizeof(tivt)); } if (argc == *argcp) incomplete_command(); addattr_nest_end(&req->n, vfinfo); *argcp = argc; *argvp = argv; return 0; }
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) { struct rtnl_handle rth; struct { struct nlmsghdr n; struct xfrm_usersa_info xsinfo; char buf[RTA_BUF_SIZE]; } req; char *idp = NULL; char *ealgop = NULL; char *aalgop = NULL; char *calgop = NULL; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); req.n.nlmsg_flags = NLM_F_REQUEST|flags; req.n.nlmsg_type = cmd; req.xsinfo.family = preferred_family; req.xsinfo.lft.soft_byte_limit = XFRM_INF; req.xsinfo.lft.hard_byte_limit = XFRM_INF; req.xsinfo.lft.soft_packet_limit = XFRM_INF; req.xsinfo.lft.hard_packet_limit = XFRM_INF; while (argc > 0) { if (strcmp(*argv, "mode") == 0) { NEXT_ARG(); xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); } else if (strcmp(*argv, "reqid") == 0) { NEXT_ARG(); xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); } else if (strcmp(*argv, "seq") == 0) { NEXT_ARG(); xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); } else if (strcmp(*argv, "replay-window") == 0) { NEXT_ARG(); if (get_u8(&req.xsinfo.replay_window, *argv, 0)) invarg("\"replay-window\" value is invalid", *argv); } else if (strcmp(*argv, "flag") == 0) { NEXT_ARG(); xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); } else if (strcmp(*argv, "sel") == 0) { NEXT_ARG(); xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); } else if (strcmp(*argv, "limit") == 0) { NEXT_ARG(); xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); } else if (strcmp(*argv, "encap") == 0) { struct xfrm_encap_tmpl encap; inet_prefix oa; NEXT_ARG(); xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); NEXT_ARG(); if (get_u16(&encap.encap_sport, *argv, 0)) invarg("\"encap\" sport value is invalid", *argv); encap.encap_sport = htons(encap.encap_sport); NEXT_ARG(); if (get_u16(&encap.encap_dport, *argv, 0)) invarg("\"encap\" dport value is invalid", *argv); encap.encap_dport = htons(encap.encap_dport); NEXT_ARG(); get_addr(&oa, *argv, AF_UNSPEC); memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, (void *)&encap, sizeof(encap)); } else { /* try to assume ALGO */ int type = xfrm_algotype_getbyname(*argv); switch (type) { case XFRMA_ALG_CRYPT: case XFRMA_ALG_AUTH: case XFRMA_ALG_COMP: { /* ALGO */ struct { struct xfrm_algo alg; char buf[XFRM_ALGO_KEY_BUF_SIZE]; } alg; int len; char *name; char *key; switch (type) { case XFRMA_ALG_CRYPT: if (ealgop) duparg("ALGOTYPE", *argv); ealgop = *argv; break; case XFRMA_ALG_AUTH: if (aalgop) duparg("ALGOTYPE", *argv); aalgop = *argv; break; case XFRMA_ALG_COMP: if (calgop) duparg("ALGOTYPE", *argv); calgop = *argv; break; default: /* not reached */ invarg("\"ALGOTYPE\" is invalid\n", *argv); } if (!NEXT_ARG_OK()) missarg("ALGONAME"); NEXT_ARG(); name = *argv; if (!NEXT_ARG_OK()) missarg("ALGOKEY"); NEXT_ARG(); key = *argv; memset(&alg, 0, sizeof(alg)); xfrm_algo_parse((void *)&alg, type, name, key, sizeof(alg.buf)); len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len; addattr_l(&req.n, sizeof(req.buf), type, (void *)&alg, len); break; } default: /* try to assume ID */ if (idp) invarg("unknown", *argv); idp = *argv; /* ID */ xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, &req.xsinfo.family, 0, &argc, &argv); if (preferred_family == AF_UNSPEC) preferred_family = req.xsinfo.family; } } argc--; argv++; } if (!idp) { // fprintf(stderr, "Not enough information: \"ID\" is required\n"); exit(1); } if (ealgop || aalgop || calgop) { if (req.xsinfo.id.proto != IPPROTO_ESP && req.xsinfo.id.proto != IPPROTO_AH && req.xsinfo.id.proto != IPPROTO_COMP) { // fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } else { if (req.xsinfo.id.proto == IPPROTO_ESP || req.xsinfo.id.proto == IPPROTO_AH || req.xsinfo.id.proto == IPPROTO_COMP) { // fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit (1); } } if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) exit(1); if (req.xsinfo.family == AF_UNSPEC) req.xsinfo.family = AF_INET; if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) exit(2); rtnl_close(&rth); return 0; }
static int iplink_parse_vf(int vf, int *argcp, char ***argvp, struct iplink_req *req) { int len, argc = *argcp; char **argv = *argvp; struct rtattr *vfinfo; vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO); while (NEXT_ARG_OK()) { NEXT_ARG(); if (matches(*argv, "mac") == 0) { struct ifla_vf_mac ivm; NEXT_ARG(); ivm.vf = vf; len = ll_addr_a2n((char *)ivm.mac, 32, *argv); if (len < 0) return -1; addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm)); } else if (matches(*argv, "vlan") == 0) { struct ifla_vf_vlan ivv; NEXT_ARG(); if (get_unsigned(&ivv.vlan, *argv, 0)) { invarg("Invalid \"vlan\" value\n", *argv); } ivv.vf = vf; ivv.qos = 0; if (NEXT_ARG_OK()) { NEXT_ARG(); if (matches(*argv, "qos") == 0) { NEXT_ARG(); if (get_unsigned(&ivv.qos, *argv, 0)) { invarg("Invalid \"qos\" value\n", *argv); } } else { /* rewind arg */ PREV_ARG(); } } addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv)); } else if (matches(*argv, "rate") == 0) { struct ifla_vf_tx_rate ivt; NEXT_ARG(); if (get_unsigned(&ivt.rate, *argv, 0)) { invarg("Invalid \"rate\" value\n", *argv); } ivt.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_TX_RATE, &ivt, sizeof(ivt)); } else if (matches(*argv, "spoofchk") == 0) { struct ifla_vf_spoofchk ivs; NEXT_ARG(); if (matches(*argv, "on") == 0) ivs.setting = 1; else if (matches(*argv, "off") == 0) ivs.setting = 0; else invarg("Invalid \"spoofchk\" value\n", *argv); ivs.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); } else { /* rewind arg */ PREV_ARG(); break; } } if (argc == *argcp) incomplete_command(); addattr_nest_end(&req->n, vfinfo); *argcp = argc; *argvp = argv; return 0; }
static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) { struct rtnl_handle rth; struct { struct nlmsghdr n; struct xfrm_usersa_info xsinfo; char buf[RTA_BUF_SIZE]; } req; struct xfrm_replay_state replay; struct xfrm_replay_state_esn replay_esn; __u32 replay_window = 0; __u32 seq = 0, oseq = 0, seq_hi = 0, oseq_hi = 0; char *idp = NULL; char *aeadop = NULL; char *ealgop = NULL; char *aalgop = NULL; char *calgop = NULL; char *coap = NULL; char *sctxp = NULL; __u32 extra_flags = 0; struct xfrm_mark mark = {0, 0}; struct { struct xfrm_user_sec_ctx sctx; char str[CTX_BUF_SIZE]; } ctx; memset(&req, 0, sizeof(req)); memset(&replay, 0, sizeof(replay)); memset(&replay_esn, 0, sizeof(replay_esn)); memset(&ctx, 0, sizeof(ctx)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); req.n.nlmsg_flags = NLM_F_REQUEST|flags; req.n.nlmsg_type = cmd; req.xsinfo.family = preferred_family; req.xsinfo.lft.soft_byte_limit = XFRM_INF; req.xsinfo.lft.hard_byte_limit = XFRM_INF; req.xsinfo.lft.soft_packet_limit = XFRM_INF; req.xsinfo.lft.hard_packet_limit = XFRM_INF; while (argc > 0) { if (strcmp(*argv, "mode") == 0) { NEXT_ARG(); xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); } else if (strcmp(*argv, "mark") == 0) { xfrm_parse_mark(&mark, &argc, &argv); } else if (strcmp(*argv, "reqid") == 0) { NEXT_ARG(); xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); } else if (strcmp(*argv, "seq") == 0) { NEXT_ARG(); xfrm_seq_parse(&req.xsinfo.seq, &argc, &argv); } else if (strcmp(*argv, "replay-window") == 0) { NEXT_ARG(); if (get_u32(&replay_window, *argv, 0)) invarg("value after \"replay-window\" is invalid", *argv); } else if (strcmp(*argv, "replay-seq") == 0) { NEXT_ARG(); if (get_u32(&seq, *argv, 0)) invarg("value after \"replay-seq\" is invalid", *argv); } else if (strcmp(*argv, "replay-seq-hi") == 0) { NEXT_ARG(); if (get_u32(&seq_hi, *argv, 0)) invarg("value after \"replay-seq-hi\" is invalid", *argv); } else if (strcmp(*argv, "replay-oseq") == 0) { NEXT_ARG(); if (get_u32(&oseq, *argv, 0)) invarg("value after \"replay-oseq\" is invalid", *argv); } else if (strcmp(*argv, "replay-oseq-hi") == 0) { NEXT_ARG(); if (get_u32(&oseq_hi, *argv, 0)) invarg("value after \"replay-oseq-hi\" is invalid", *argv); } else if (strcmp(*argv, "flag") == 0) { NEXT_ARG(); xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); } else if (strcmp(*argv, "extra-flag") == 0) { NEXT_ARG(); xfrm_state_extra_flag_parse(&extra_flags, &argc, &argv); } else if (strcmp(*argv, "sel") == 0) { NEXT_ARG(); preferred_family = AF_UNSPEC; xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); preferred_family = req.xsinfo.sel.family; } else if (strcmp(*argv, "limit") == 0) { NEXT_ARG(); xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); } else if (strcmp(*argv, "encap") == 0) { struct xfrm_encap_tmpl encap; inet_prefix oa; NEXT_ARG(); xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); NEXT_ARG(); if (get_u16(&encap.encap_sport, *argv, 0)) invarg("SPORT value after \"encap\" is invalid", *argv); encap.encap_sport = htons(encap.encap_sport); NEXT_ARG(); if (get_u16(&encap.encap_dport, *argv, 0)) invarg("DPORT value after \"encap\" is invalid", *argv); encap.encap_dport = htons(encap.encap_dport); NEXT_ARG(); get_addr(&oa, *argv, AF_UNSPEC); memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, (void *)&encap, sizeof(encap)); } else if (strcmp(*argv, "coa") == 0) { inet_prefix coa; xfrm_address_t xcoa; if (coap) duparg("coa", *argv); coap = *argv; NEXT_ARG(); get_prefix(&coa, *argv, preferred_family); if (coa.family == AF_UNSPEC) invarg("value after \"coa\" has an unrecognized address family", *argv); if (coa.bytelen > sizeof(xcoa)) invarg("value after \"coa\" is too large", *argv); memset(&xcoa, 0, sizeof(xcoa)); memcpy(&xcoa, &coa.data, coa.bytelen); addattr_l(&req.n, sizeof(req.buf), XFRMA_COADDR, (void *)&xcoa, sizeof(xcoa)); } else if (strcmp(*argv, "ctx") == 0) { char *context; if (sctxp) duparg("ctx", *argv); sctxp = *argv; NEXT_ARG(); context = *argv; xfrm_sctx_parse((char *)&ctx.str, context, &ctx.sctx); addattr_l(&req.n, sizeof(req.buf), XFRMA_SEC_CTX, (void *)&ctx, ctx.sctx.len); } else { /* try to assume ALGO */ int type = xfrm_algotype_getbyname(*argv); switch (type) { case XFRMA_ALG_AEAD: case XFRMA_ALG_CRYPT: case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH_TRUNC: case XFRMA_ALG_COMP: { /* ALGO */ struct { union { struct xfrm_algo alg; struct xfrm_algo_aead aead; struct xfrm_algo_auth auth; } u; char buf[XFRM_ALGO_KEY_BUF_SIZE]; } alg = {}; int len; __u32 icvlen, trunclen; char *name; char *key = ""; char *buf; switch (type) { case XFRMA_ALG_AEAD: if (ealgop || aalgop || aeadop) duparg("ALGO-TYPE", *argv); aeadop = *argv; break; case XFRMA_ALG_CRYPT: if (ealgop || aeadop) duparg("ALGO-TYPE", *argv); ealgop = *argv; break; case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH_TRUNC: if (aalgop || aeadop) duparg("ALGO-TYPE", *argv); aalgop = *argv; break; case XFRMA_ALG_COMP: if (calgop) duparg("ALGO-TYPE", *argv); calgop = *argv; break; default: /* not reached */ invarg("ALGO-TYPE value is invalid\n", *argv); } if (!NEXT_ARG_OK()) missarg("ALGO-NAME"); NEXT_ARG(); name = *argv; switch (type) { case XFRMA_ALG_AEAD: case XFRMA_ALG_CRYPT: case XFRMA_ALG_AUTH: case XFRMA_ALG_AUTH_TRUNC: if (!NEXT_ARG_OK()) missarg("ALGO-KEYMAT"); NEXT_ARG(); key = *argv; break; } buf = alg.u.alg.alg_key; len = sizeof(alg.u.alg); switch (type) { case XFRMA_ALG_AEAD: if (!NEXT_ARG_OK()) missarg("ALGO-ICV-LEN"); NEXT_ARG(); if (get_u32(&icvlen, *argv, 0)) invarg("ALGO-ICV-LEN value is invalid", *argv); alg.u.aead.alg_icv_len = icvlen; buf = alg.u.aead.alg_key; len = sizeof(alg.u.aead); break; case XFRMA_ALG_AUTH_TRUNC: if (!NEXT_ARG_OK()) missarg("ALGO-TRUNC-LEN"); NEXT_ARG(); if (get_u32(&trunclen, *argv, 0)) invarg("ALGO-TRUNC-LEN value is invalid", *argv); alg.u.auth.alg_trunc_len = trunclen; buf = alg.u.auth.alg_key; len = sizeof(alg.u.auth); break; } xfrm_algo_parse((void *)&alg, type, name, key, buf, sizeof(alg.buf)); len += alg.u.alg.alg_key_len; addattr_l(&req.n, sizeof(req.buf), type, (void *)&alg, len); break; } default: /* try to assume ID */ if (idp) invarg("unknown", *argv); idp = *argv; /* ID */ xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, &req.xsinfo.family, 0, &argc, &argv); if (preferred_family == AF_UNSPEC) preferred_family = req.xsinfo.family; } } argc--; argv++; } if (req.xsinfo.flags & XFRM_STATE_ESN && replay_window == 0) { fprintf(stderr, "Error: esn flag set without replay-window.\n"); exit(-1); } if (replay_window > XFRMA_REPLAY_ESN_MAX) { fprintf(stderr, "Error: replay-window (%u) > XFRMA_REPLAY_ESN_MAX (%u).\n", replay_window, XFRMA_REPLAY_ESN_MAX); exit(-1); } if (req.xsinfo.flags & XFRM_STATE_ESN || replay_window > (sizeof(replay.bitmap) * 8)) { replay_esn.seq = seq; replay_esn.oseq = oseq; replay_esn.seq_hi = seq_hi; replay_esn.oseq_hi = oseq_hi; replay_esn.replay_window = replay_window; replay_esn.bmp_len = (replay_window + sizeof(__u32) * 8 - 1) / (sizeof(__u32) * 8); addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_ESN_VAL, &replay_esn, sizeof(replay_esn)); } else { if (seq || oseq) { replay.seq = seq; replay.oseq = oseq; addattr_l(&req.n, sizeof(req.buf), XFRMA_REPLAY_VAL, &replay, sizeof(replay)); } req.xsinfo.replay_window = replay_window; } if (extra_flags) addattr32(&req.n, sizeof(req.buf), XFRMA_SA_EXTRA_FLAGS, extra_flags); if (!idp) { fprintf(stderr, "Not enough information: ID is required\n"); exit(1); } if (mark.m) { int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK, (void *)&mark, sizeof(mark)); if (r < 0) { fprintf(stderr, "XFRMA_MARK failed\n"); exit(1); } } if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) { switch (req.xsinfo.mode) { case XFRM_MODE_TRANSPORT: case XFRM_MODE_TUNNEL: break; case XFRM_MODE_BEET: if (req.xsinfo.id.proto == IPPROTO_ESP) break; default: fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } switch (req.xsinfo.id.proto) { case IPPROTO_ESP: if (calgop) { fprintf(stderr, "ALGO-TYPE value \"%s\" is invalid with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_COMP), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } if (!ealgop && !aeadop) { fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_CRYPT), strxf_algotype(XFRMA_ALG_AEAD), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } break; case IPPROTO_AH: if (ealgop || aeadop || calgop) { fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_CRYPT), strxf_algotype(XFRMA_ALG_AEAD), strxf_algotype(XFRMA_ALG_COMP), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } if (!aalgop) { fprintf(stderr, "ALGO-TYPE value \"%s\" or \"%s\" is required with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_AUTH), strxf_algotype(XFRMA_ALG_AUTH_TRUNC), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } break; case IPPROTO_COMP: if (ealgop || aalgop || aeadop) { fprintf(stderr, "ALGO-TYPE values \"%s\", \"%s\", \"%s\", and \"%s\" are invalid with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_CRYPT), strxf_algotype(XFRMA_ALG_AUTH), strxf_algotype(XFRMA_ALG_AUTH_TRUNC), strxf_algotype(XFRMA_ALG_AEAD), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } if (!calgop) { fprintf(stderr, "ALGO-TYPE value \"%s\" is required with XFRM-PROTO value \"%s\"\n", strxf_algotype(XFRMA_ALG_COMP), strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } break; } } else { if (ealgop || aalgop || aeadop || calgop) { fprintf(stderr, "ALGO is invalid with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } if (xfrm_xfrmproto_is_ro(req.xsinfo.id.proto)) { switch (req.xsinfo.mode) { case XFRM_MODE_ROUTEOPTIMIZATION: case XFRM_MODE_IN_TRIGGER: break; case 0: fprintf(stderr, "\"mode\" is required with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); default: fprintf(stderr, "MODE value is invalid with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } if (!coap) { fprintf(stderr, "\"coa\" is required with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } else { if (coap) { fprintf(stderr, "\"coa\" is invalid with XFRM-PROTO value \"%s\"\n", strxf_xfrmproto(req.xsinfo.id.proto)); exit(1); } } if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) exit(1); if (req.xsinfo.family == AF_UNSPEC) req.xsinfo.family = AF_INET; if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) exit(2); rtnl_close(&rth); return 0; }
static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) { int dist_size = 0; int slot_dist_size = 0; struct rtattr *tail; struct tc_netem_qopt opt = { .limit = 1000 }; struct tc_netem_corr cor = {}; struct tc_netem_reorder reorder = {}; struct tc_netem_corrupt corrupt = {}; struct tc_netem_gimodel gimodel; struct tc_netem_gemodel gemodel; struct tc_netem_rate rate = {}; struct tc_netem_slot slot = {}; __s16 *dist_data = NULL; __s16 *slot_dist_data = NULL; __u16 loss_type = NETEM_LOSS_UNSPEC; int present[__TCA_NETEM_MAX] = {}; __u64 rate64 = 0; for ( ; argc > 0; --argc, ++argv) { if (matches(*argv, "limit") == 0) { NEXT_ARG(); if (get_size(&opt.limit, *argv)) { explain1("limit"); return -1; } } else if (matches(*argv, "latency") == 0 || matches(*argv, "delay") == 0) { NEXT_ARG(); if (get_ticks(&opt.latency, *argv)) { explain1("latency"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); if (get_ticks(&opt.jitter, *argv)) { explain1("latency"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); ++present[TCA_NETEM_CORR]; if (get_percent(&cor.delay_corr, *argv)) { explain1("latency"); return -1; } } } } else if (matches(*argv, "loss") == 0 || matches(*argv, "drop") == 0) { if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) { explain1("duplicate loss argument\n"); return -1; } NEXT_ARG(); /* Old (deprecated) random loss model syntax */ if (isdigit(argv[0][0])) goto random_loss_model; if (!strcmp(*argv, "random")) { NEXT_ARG(); random_loss_model: if (get_percent(&opt.loss, *argv)) { explain1("loss percent"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); ++present[TCA_NETEM_CORR]; if (get_percent(&cor.loss_corr, *argv)) { explain1("loss correllation"); return -1; } } } else if (!strcmp(*argv, "state")) { double p13; NEXT_ARG(); if (parse_percent(&p13, *argv)) { explain1("loss p13"); return -1; } /* set defaults */ set_percent(&gimodel.p13, p13); set_percent(&gimodel.p31, 1. - p13); set_percent(&gimodel.p32, 0); set_percent(&gimodel.p23, 1.); set_percent(&gimodel.p14, 0); loss_type = NETEM_LOSS_GI; if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gimodel.p31, *argv)) { explain1("loss p31"); return -1; } if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gimodel.p32, *argv)) { explain1("loss p32"); return -1; } if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gimodel.p23, *argv)) { explain1("loss p23"); return -1; } if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gimodel.p14, *argv)) { explain1("loss p14"); return -1; } } else if (!strcmp(*argv, "gemodel")) { NEXT_ARG(); if (get_percent(&gemodel.p, *argv)) { explain1("loss gemodel p"); return -1; } /* set defaults */ set_percent(&gemodel.r, 1.); set_percent(&gemodel.h, 0); set_percent(&gemodel.k1, 0); loss_type = NETEM_LOSS_GE; if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gemodel.r, *argv)) { explain1("loss gemodel r"); return -1; } if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gemodel.h, *argv)) { explain1("loss gemodel h"); return -1; } /* netem option is "1-h" but kernel * expects "h". */ gemodel.h = UINT32_MAX - gemodel.h; if (!NEXT_IS_NUMBER()) continue; NEXT_ARG(); if (get_percent(&gemodel.k1, *argv)) { explain1("loss gemodel k"); return -1; } } else { fprintf(stderr, "Unknown loss parameter: %s\n", *argv); return -1; } } else if (matches(*argv, "ecn") == 0) { present[TCA_NETEM_ECN] = 1; } else if (matches(*argv, "reorder") == 0) { NEXT_ARG(); present[TCA_NETEM_REORDER] = 1; if (get_percent(&reorder.probability, *argv)) { explain1("reorder"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); ++present[TCA_NETEM_CORR]; if (get_percent(&reorder.correlation, *argv)) { explain1("reorder"); return -1; } } } else if (matches(*argv, "corrupt") == 0) { NEXT_ARG(); present[TCA_NETEM_CORRUPT] = 1; if (get_percent(&corrupt.probability, *argv)) { explain1("corrupt"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); ++present[TCA_NETEM_CORR]; if (get_percent(&corrupt.correlation, *argv)) { explain1("corrupt"); return -1; } } } else if (matches(*argv, "gap") == 0) { NEXT_ARG(); if (get_u32(&opt.gap, *argv, 0)) { explain1("gap"); return -1; } } else if (matches(*argv, "duplicate") == 0) { NEXT_ARG(); if (get_percent(&opt.duplicate, *argv)) { explain1("duplicate"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); if (get_percent(&cor.dup_corr, *argv)) { explain1("duplicate"); return -1; } } } else if (matches(*argv, "distribution") == 0) { NEXT_ARG(); dist_data = calloc(sizeof(dist_data[0]), MAX_DIST); dist_size = get_distribution(*argv, dist_data, MAX_DIST); if (dist_size <= 0) { free(dist_data); return -1; } } else if (matches(*argv, "rate") == 0) { ++present[TCA_NETEM_RATE]; NEXT_ARG(); if (strchr(*argv, '%')) { if (get_percent_rate64(&rate64, *argv, dev)) { explain1("rate"); return -1; } } else if (get_rate64(&rate64, *argv)) { explain1("rate"); return -1; } if (NEXT_IS_SIGNED_NUMBER()) { NEXT_ARG(); if (get_s32(&rate.packet_overhead, *argv, 0)) { explain1("rate"); return -1; } } if (NEXT_IS_NUMBER()) { NEXT_ARG(); if (get_u32(&rate.cell_size, *argv, 0)) { explain1("rate"); return -1; } } if (NEXT_IS_SIGNED_NUMBER()) { NEXT_ARG(); if (get_s32(&rate.cell_overhead, *argv, 0)) { explain1("rate"); return -1; } } } else if (matches(*argv, "slot") == 0) { if (NEXT_IS_NUMBER()) { NEXT_ARG(); present[TCA_NETEM_SLOT] = 1; if (get_time64(&slot.min_delay, *argv)) { explain1("slot min_delay"); return -1; } if (NEXT_IS_NUMBER()) { NEXT_ARG(); if (get_time64(&slot.max_delay, *argv) || slot.max_delay < slot.min_delay) { explain1("slot max_delay"); return -1; } } else { slot.max_delay = slot.min_delay; } } else { NEXT_ARG(); if (strcmp(*argv, "distribution") == 0) { present[TCA_NETEM_SLOT] = 1; NEXT_ARG(); slot_dist_data = calloc(sizeof(slot_dist_data[0]), MAX_DIST); if (!slot_dist_data) return -1; slot_dist_size = get_distribution(*argv, slot_dist_data, MAX_DIST); if (slot_dist_size <= 0) { free(slot_dist_data); return -1; } NEXT_ARG(); if (get_time64(&slot.dist_delay, *argv)) { explain1("slot delay"); return -1; } NEXT_ARG(); if (get_time64(&slot.dist_jitter, *argv)) { explain1("slot jitter"); return -1; } if (slot.dist_jitter <= 0) { fprintf(stderr, "Non-positive jitter\n"); return -1; } } else { fprintf(stderr, "Unknown slot parameter: %s\n", *argv); return -1; } } if (NEXT_ARG_OK() && matches(*(argv+1), "packets") == 0) { NEXT_ARG(); if (!NEXT_ARG_OK() || get_s32(&slot.max_packets, *(argv+1), 0)) { explain1("slot packets"); return -1; } NEXT_ARG(); } if (NEXT_ARG_OK() && matches(*(argv+1), "bytes") == 0) { unsigned int max_bytes; NEXT_ARG(); if (!NEXT_ARG_OK() || get_size(&max_bytes, *(argv+1))) { explain1("slot bytes"); return -1; } slot.max_bytes = (int) max_bytes; NEXT_ARG(); } } else if (strcmp(*argv, "help") == 0) { explain(); return -1; } else { fprintf(stderr, "What is \"%s\"?\n", *argv); explain(); return -1; } } tail = NLMSG_TAIL(n); if (reorder.probability) { if (opt.latency == 0) { fprintf(stderr, "reordering not possible without specifying some delay\n"); explain(); return -1; } if (opt.gap == 0) opt.gap = 1; } else if (opt.gap > 0) { fprintf(stderr, "gap specified without reorder probability\n"); explain(); return -1; } if (present[TCA_NETEM_ECN]) { if (opt.loss <= 0 && loss_type == NETEM_LOSS_UNSPEC) { fprintf(stderr, "ecn requested without loss model\n"); explain(); return -1; } } if (dist_data && (opt.latency == 0 || opt.jitter == 0)) { fprintf(stderr, "distribution specified but no latency and jitter values\n"); explain(); return -1; } if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0) return -1; if (present[TCA_NETEM_CORR] && addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0) return -1; if (present[TCA_NETEM_REORDER] && addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0) return -1; if (present[TCA_NETEM_ECN] && addattr_l(n, 1024, TCA_NETEM_ECN, &present[TCA_NETEM_ECN], sizeof(present[TCA_NETEM_ECN])) < 0) return -1; if (present[TCA_NETEM_CORRUPT] && addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0) return -1; if (present[TCA_NETEM_SLOT] && addattr_l(n, 1024, TCA_NETEM_SLOT, &slot, sizeof(slot)) < 0) return -1; if (loss_type != NETEM_LOSS_UNSPEC) { struct rtattr *start; start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED); if (loss_type == NETEM_LOSS_GI) { if (addattr_l(n, 1024, NETEM_LOSS_GI, &gimodel, sizeof(gimodel)) < 0) return -1; } else if (loss_type == NETEM_LOSS_GE) { if (addattr_l(n, 1024, NETEM_LOSS_GE, &gemodel, sizeof(gemodel)) < 0) return -1; } else { fprintf(stderr, "loss in the weeds!\n"); return -1; } addattr_nest_end(n, start); } if (present[TCA_NETEM_RATE]) { if (rate64 >= (1ULL << 32)) { if (addattr_l(n, 1024, TCA_NETEM_RATE64, &rate64, sizeof(rate64)) < 0) return -1; rate.rate = ~0U; } else { rate.rate = rate64; } if (addattr_l(n, 1024, TCA_NETEM_RATE, &rate, sizeof(rate)) < 0) return -1; } if (dist_data) { if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]), TCA_NETEM_DELAY_DIST, dist_data, dist_size * sizeof(dist_data[0])) < 0) return -1; free(dist_data); } if (slot_dist_data) { if (addattr_l(n, MAX_DIST * sizeof(slot_dist_data[0]), TCA_NETEM_SLOT_DIST, slot_dist_data, slot_dist_size * sizeof(slot_dist_data[0])) < 0) return -1; free(slot_dist_data); } tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; return 0; } static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) { const struct tc_netem_corr *cor = NULL; const struct tc_netem_reorder *reorder = NULL; const struct tc_netem_corrupt *corrupt = NULL; const struct tc_netem_gimodel *gimodel = NULL; const struct tc_netem_gemodel *gemodel = NULL; int *ecn = NULL; struct tc_netem_qopt qopt; const struct tc_netem_rate *rate = NULL; const struct tc_netem_slot *slot = NULL; int len; __u64 rate64 = 0; SPRINT_BUF(b1); if (opt == NULL) return 0; len = RTA_PAYLOAD(opt) - sizeof(qopt); if (len < 0) { fprintf(stderr, "options size error\n"); return -1; } memcpy(&qopt, RTA_DATA(opt), sizeof(qopt)); if (len > 0) { struct rtattr *tb[TCA_NETEM_MAX+1]; parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt), len); if (tb[TCA_NETEM_CORR]) { if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor)) return -1; cor = RTA_DATA(tb[TCA_NETEM_CORR]); } if (tb[TCA_NETEM_REORDER]) { if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder)) return -1; reorder = RTA_DATA(tb[TCA_NETEM_REORDER]); } if (tb[TCA_NETEM_CORRUPT]) { if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt)) return -1; corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]); } if (tb[TCA_NETEM_LOSS]) { struct rtattr *lb[NETEM_LOSS_MAX + 1]; parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]); if (lb[NETEM_LOSS_GI]) gimodel = RTA_DATA(lb[NETEM_LOSS_GI]); if (lb[NETEM_LOSS_GE]) gemodel = RTA_DATA(lb[NETEM_LOSS_GE]); } if (tb[TCA_NETEM_RATE]) { if (RTA_PAYLOAD(tb[TCA_NETEM_RATE]) < sizeof(*rate)) return -1; rate = RTA_DATA(tb[TCA_NETEM_RATE]); } if (tb[TCA_NETEM_ECN]) { if (RTA_PAYLOAD(tb[TCA_NETEM_ECN]) < sizeof(*ecn)) return -1; ecn = RTA_DATA(tb[TCA_NETEM_ECN]); } if (tb[TCA_NETEM_RATE64]) { if (RTA_PAYLOAD(tb[TCA_NETEM_RATE64]) < sizeof(rate64)) return -1; rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]); } if (tb[TCA_NETEM_SLOT]) { if (RTA_PAYLOAD(tb[TCA_NETEM_SLOT]) < sizeof(*slot)) return -1; slot = RTA_DATA(tb[TCA_NETEM_SLOT]); } } fprintf(f, "limit %d", qopt.limit); if (qopt.latency) { fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1)); if (qopt.jitter) { fprintf(f, " %s", sprint_ticks(qopt.jitter, b1)); if (cor && cor->delay_corr) fprintf(f, " %s", sprint_percent(cor->delay_corr, b1)); } } if (qopt.loss) { fprintf(f, " loss %s", sprint_percent(qopt.loss, b1)); if (cor && cor->loss_corr) fprintf(f, " %s", sprint_percent(cor->loss_corr, b1)); } if (gimodel) { fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1)); fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1)); fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1)); fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1)); fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1)); } if (gemodel) { fprintf(f, " loss gemodel p %s", sprint_percent(gemodel->p, b1)); fprintf(f, " r %s", sprint_percent(gemodel->r, b1)); fprintf(f, " 1-h %s", sprint_percent(UINT32_MAX - gemodel->h, b1)); fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1)); } if (qopt.duplicate) { fprintf(f, " duplicate %s", sprint_percent(qopt.duplicate, b1)); if (cor && cor->dup_corr) fprintf(f, " %s", sprint_percent(cor->dup_corr, b1)); } if (reorder && reorder->probability) { fprintf(f, " reorder %s", sprint_percent(reorder->probability, b1)); if (reorder->correlation) fprintf(f, " %s", sprint_percent(reorder->correlation, b1)); } if (corrupt && corrupt->probability) { fprintf(f, " corrupt %s", sprint_percent(corrupt->probability, b1)); if (corrupt->correlation) fprintf(f, " %s", sprint_percent(corrupt->correlation, b1)); } if (rate && rate->rate) { if (rate64) fprintf(f, " rate %s", sprint_rate(rate64, b1)); else fprintf(f, " rate %s", sprint_rate(rate->rate, b1)); if (rate->packet_overhead) fprintf(f, " packetoverhead %d", rate->packet_overhead); if (rate->cell_size) fprintf(f, " cellsize %u", rate->cell_size); if (rate->cell_overhead) fprintf(f, " celloverhead %d", rate->cell_overhead); } if (slot) { if (slot->dist_jitter > 0) { fprintf(f, " slot distribution %s", sprint_time64(slot->dist_delay, b1)); fprintf(f, " %s", sprint_time64(slot->dist_jitter, b1)); } else { fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1)); fprintf(f, " %s", sprint_time64(slot->max_delay, b1)); } if (slot->max_packets) fprintf(f, " packets %d", slot->max_packets); if (slot->max_bytes) fprintf(f, " bytes %d", slot->max_bytes); } if (ecn) fprintf(f, " ecn "); if (qopt.gap) fprintf(f, " gap %lu", (unsigned long)qopt.gap); return 0; } struct qdisc_util netem_qdisc_util = { .id = "netem", .parse_qopt = netem_parse_opt, .print_qopt = netem_print_opt, };