jsonw_string(jw_global, "router");
}
if (r->ndm_flags & NTF_EXT_LEARNED) {
+ start_json_fdb_flags_array(&fdb_flags);
+ jsonw_string(jw_global, "extern_learn");
+ }
+ if (r->ndm_flags & NTF_OFFLOADED) {
start_json_fdb_flags_array(&fdb_flags);
jsonw_string(jw_global, "offload");
}
if (r->ndm_flags & NTF_ROUTER)
fprintf(fp, "router ");
if (r->ndm_flags & NTF_EXT_LEARNED)
+ fprintf(fp, "extern_learn ");
+ if (r->ndm_flags & NTF_OFFLOADED)
fprintf(fp, "offload ");
if (tb[NDA_MASTER]) {
fprintf(fp, "master %s ",
BPF_PROG_ATTACH,
BPF_PROG_DETACH,
BPF_PROG_TEST_RUN,
+ BPF_PROG_GET_NEXT_ID,
+ BPF_MAP_GET_NEXT_ID,
+ BPF_PROG_GET_FD_BY_ID,
+ BPF_MAP_GET_FD_BY_ID,
+ BPF_OBJ_GET_INFO_BY_FD,
};
enum bpf_map_type {
__u32 repeat;
__u32 duration;
} test;
+
+ struct { /* anonymous struct used by BPF_*_GET_*_ID */
+ union {
+ __u32 start_id;
+ __u32 prog_id;
+ __u32 map_id;
+ };
+ __u32 next_id;
+ };
+
+ struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+ __u32 bpf_fd;
+ __u32 info_len;
+ __aligned_u64 info;
+ } info;
} __attribute__((aligned(8)));
/* BPF helper function descriptions:
* @flags: room for future extensions
* Return: 0 on success or negative error
*
- * u64 bpf_perf_event_read(&map, index)
- * Return: Number events read or error code
+ * u64 bpf_perf_event_read(map, flags)
+ * read perf event counter value
+ * @map: pointer to perf_event_array map
+ * @flags: index of event in the map or bitmask flags
+ * Return: value of perf event counter read or error code
*
* int bpf_redirect(ifindex, flags)
* redirect to another netdev
* @skb: pointer to skb
* Return: realm if != 0
*
- * int bpf_perf_event_output(ctx, map, index, data, size)
+ * int bpf_perf_event_output(ctx, map, flags, data, size)
* output perf raw sample
* @ctx: struct pt_regs*
* @map: pointer to perf_event_array map
- * @index: index of event in the map
+ * @flags: index of event in the map or bitmask flags
* @data: data on stack to be output as raw data
* @size: size of data
* Return: 0 on success or negative error
* Get the owner uid of the socket stored inside sk_buff.
* @skb: pointer to skb
* Return: uid of the socket owner on success or overflowuid if failed.
+ *
+ * u32 bpf_set_hash(skb, hash)
+ * Set full skb->hash.
+ * @skb: pointer to skb
+ * @hash: hash to set
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
FN(xdp_adjust_head), \
FN(probe_read_str), \
FN(get_socket_cookie), \
- FN(get_socket_uid),
+ FN(get_socket_uid), \
+ FN(set_hash),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
__u32 data_end;
};
+#define BPF_TAG_SIZE 8
+
+struct bpf_prog_info {
+ __u32 type;
+ __u32 id;
+ __u8 tag[BPF_TAG_SIZE];
+ __u32 jited_prog_len;
+ __u32 xlated_prog_len;
+ __aligned_u64 jited_prog_insns;
+ __aligned_u64 xlated_prog_insns;
+} __attribute__((aligned(8)));
+
+struct bpf_map_info {
+ __u32 type;
+ __u32 id;
+ __u32 key_size;
+ __u32 value_size;
+ __u32 max_entries;
+ __u32 map_flags;
+} __attribute__((aligned(8)));
+
#endif /* __LINUX_BPF_H__ */
IFLA_GSO_MAX_SIZE,
IFLA_PAD,
IFLA_XDP,
+ IFLA_EVENT,
__IFLA_MAX
};
#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1)
+enum {
+ IFLA_EVENT_NONE,
+ IFLA_EVENT_REBOOT, /* internal reset / reboot */
+ IFLA_EVENT_FEATURES, /* change in offload features */
+ IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */
+ IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */
+ IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */
+ IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */
+};
+
#endif /* _LINUX_IF_LINK_H */
#define NTF_MASTER 0x04
#define NTF_PROXY 0x08 /* == ATF_PUBL */
#define NTF_EXT_LEARNED 0x10
+#define NTF_OFFLOADED 0x20
#define NTF_ROUTER 0x80
/*
#define TC_ACT_QUEUED 5
#define TC_ACT_REPEAT 6
#define TC_ACT_REDIRECT 7
+#define TC_ACT_TRAP 8 /* For hw path, this means "trap to cpu"
+ * and don't further process the frame
+ * in hardware. For sw path, this is
+ * equivalent of TC_ACT_STOLEN - drop
+ * the skb and act like everything
+ * is alright.
+ */
/* There is a special kind of actions called "extended actions",
* which need a value parameter. These have a local opcode located in
(((combined) & (~TC_ACT_EXT_VAL_MASK)) == opcode)
#define TC_ACT_JUMP __TC_ACT_EXT(1)
+#define TC_ACT_GOTO_CHAIN __TC_ACT_EXT(2)
/* Action type identifiers*/
enum {
TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */
TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */
+ TCA_FLOWER_KEY_TCP_FLAGS, /* be16 */
+ TCA_FLOWER_KEY_TCP_FLAGS_MASK, /* be16 */
+
+ TCA_FLOWER_KEY_IP_TOS, /* u8 */
+ TCA_FLOWER_KEY_IP_TOS_MASK, /* u8 */
+ TCA_FLOWER_KEY_IP_TTL, /* u8 */
+ TCA_FLOWER_KEY_IP_TTL_MASK, /* u8 */
+
__TCA_FLOWER_MAX,
};
#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */
#define RTM_F_PREFIX 0x800 /* Prefix addresses */
#define RTM_F_LOOKUP_TABLE 0x1000 /* set rtm_table to FIB lookup result */
+#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */
/* Reserved table identifiers */
TCA_STAB,
TCA_PAD,
TCA_DUMP_INVISIBLE,
+ TCA_CHAIN,
__TCA_MAX
};
return 0;
}
+static const char *link_events[] = {
+ [IFLA_EVENT_NONE] = "NONE",
+ [IFLA_EVENT_REBOOT] = "REBOOT",
+ [IFLA_EVENT_FEATURES] = "FEATURE CHANGE",
+ [IFLA_EVENT_BONDING_FAILOVER] = "BONDING FAILOVER",
+ [IFLA_EVENT_NOTIFY_PEERS] = "NOTIFY PEERS",
+ [IFLA_EVENT_IGMP_RESEND] = "RESEND IGMP",
+ [IFLA_EVENT_BONDING_OPTIONS] = "BONDING OPTION"
+};
+
+static void print_link_event(FILE *f, __u32 event)
+{
+ if (event >= ARRAY_SIZE(link_events))
+ fprintf(f, "event %d ", event);
+ else {
+ if (event)
+ fprintf(f, "event %s ", link_events[event]);
+ }
+}
+
int print_linkinfo(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg)
{
if (filter.showqueue)
print_queuelen(fp, tb);
+ if (tb[IFLA_EVENT])
+ print_link_event(fp, rta_getattr_u32(tb[IFLA_EVENT]));
+
if (!filter.family || filter.family == AF_PACKET || show_details) {
SPRINT_BUF(b1);
fprintf(fp, "%s", _SL_);
fprintf(stderr, " ip route save SELECTOR\n");
fprintf(stderr, " ip route restore\n");
fprintf(stderr, " ip route showdump\n");
- fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n");
+ fprintf(stderr, " ip route get [ ROUTE_GET_FLAGS ] ADDRESS\n");
+ fprintf(stderr, " [ from ADDRESS iif STRING ]\n");
fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n");
fprintf(stderr, " [ mark NUMBER ] [ vrf NAME ]\n");
fprintf(stderr, " [ uid NUMBER ]\n");
fprintf(stderr, "ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n");
fprintf(stderr, "SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n");
fprintf(stderr, "SEGMODE := [ encap | inline ]\n");
+ fprintf(stderr, "ROUTE_GET_FLAGS := [ fibmatch ]\n");
exit(-1);
}
char *idev = NULL;
char *odev = NULL;
int connected = 0;
+ int fib_match = 0;
int from_ok = 0;
unsigned int mark = 0;
if (get_unsigned(&uid, *argv, 0))
invarg("invalid UID\n", *argv);
addattr32(&req.n, sizeof(req), RTA_UID, uid);
+ } else if (matches(*argv, "fibmatch") == 0) {
+ fib_match = 1;
} else {
inet_prefix addr;
req.r.rtm_family = AF_INET;
req.r.rtm_flags |= RTM_F_LOOKUP_TABLE;
+ if (fib_match)
+ req.r.rtm_flags |= RTM_F_FIB_MATCH;
if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
return -2;
.ti -8
.B ip route get
+.I ROUTE_GET_FLAGS
.IR ADDRESS " [ "
.BI from " ADDRESS " iif " STRING"
.RB " ] [ " oif
.B hmac
.IR KEYID " ]"
+.ti -8
+.IR ROUTE_GET_FLAGS " := "
+.BR " [ "
+.BR fibmatch
+.BR " ] "
+
.SH DESCRIPTION
.B ip route
is used to manipulate entries in the kernel routing tables.
this command gets a single route to a destination and prints its
contents exactly as the kernel sees it.
+.TP
+.BI fibmatch
+Return full fib lookup matched route. Default is to return the resolved
+dst entry
+
.TP
.BI to " ADDRESS " (default)
the destination address.
.BR vlan_ethtype " { " ipv4 " | " ipv6 " | "
.IR ETH_TYPE " } | "
.BR ip_proto " { " tcp " | " udp " | " sctp " | " icmp " | " icmpv6 " | "
-.IR IP_PROTO " } | { "
+.IR IP_PROTO " } | "
+.B ip_tos
+.IR MASKED_IP_TOS " | "
+.B ip_ttl
+.IR MASKED_IP_TTL " | { "
.BR dst_ip " | " src_ip " } "
.IR PREFIX " | { "
.BR dst_port " | " src_port " } "
.IR port_number " } | "
+.B tcp_flags
+.IR MASKED_TCP_FLAGS " | "
.B type
.IR MASKED_TYPE " | "
.B code
.BR tcp ", " udp ", " sctp ", " icmp ", " icmpv6
or an unsigned 8bit value in hexadecimal format.
.TP
+.BI ip_tos " MASKED_IP_TOS"
+Match on ipv4 TOS or ipv6 traffic-class - eight bits in hexadecimal format.
+A mask may be optionally provided to limit the bits which are matched. A mask
+is provided by following the value with a slash and then the mask. If the mask
+is missing then a match on all bits is assumed.
+.TP
+.BI ip_ttl " MASKED_IP_TTL"
+Match on ipv4 TTL or ipv6 hop-limit - eight bits value in decimal or hexadecimal format.
+A mask may be optionally provided to limit the bits which are matched. Same
+logic is used for the mask as with matching on ip_tos.
+.TP
.BI dst_ip " PREFIX"
.TQ
.BI src_ip " PREFIX"
.BR ip_proto " values " udp ", " tcp " and " sctp
which have to be specified in beforehand.
.TP
+.BI tcp_flags " MASKED_TCP_FLAGS"
+Match on TCP flags represented as 12bit bitfield in in hexadecimal format.
+A mask may be optionally provided to limit the bits which are matched. A mask
+is provided by following the value with a slash and then the mask. If the mask
+is missing then a match on all bits is assumed.
+.TP
.BI type " MASKED_TYPE"
.TQ
.BI code " MASKED_CODE"
.ti -8
.IR CONTROL " := { "
-.BR reclassify " | " use " | " pipe " | " drop " | " continue " | " ok " }"
+.BR reclassify " | " use " | " pipe " | " drop " | " continue " | " ok " | " goto " " chain " " CHAIN_INDEX " }"
.SH DESCRIPTION
The
.B ife
.ti -8
.IR CONTROL " := {"
-.BR reclassify " | " pipe " | " drop " | " shot " | " continue " | " pass " }"
+.BR reclassify " | " pipe " | " drop " | " shot " | " continue " | " pass " | " goto " " chain " " CHAIN_INDEX " }"
.SH DESCRIPTION
The
.B pedit
.ti -8
.IR EXCEEDACT/NOTEXCEEDACT " := { "
-.BR pipe " | " ok " | " reclassify " | " drop " | " continue " }"
+.BR pipe " | " ok " | " reclassify " | " drop " | " continue " | " goto " " chain " " CHAIN_INDEX " }"
.SH DESCRIPTION
The
.B police
.ti -8
.IR CONTROL " := { "
-.BR reclassify " | " pipe " | " drop " | " continue " | " pass " }"
+.BR reclassify " | " pipe " | " drop " | " continue " | " pass " | " goto " " chain " " CHAIN_INDEX " }"
.SH DESCRIPTION
The
.B vlan
" dst_mac MASKED-LLADDR |\n"
" src_mac MASKED-LLADDR |\n"
" ip_proto [tcp | udp | sctp | icmp | icmpv6 | IP-PROTO ] |\n"
+ " ip_tos MASKED-IP_TOS |\n"
+ " ip_ttl MASKED-IP_TTL |\n"
" dst_ip PREFIX |\n"
" src_ip PREFIX |\n"
" dst_port PORT-NUMBER |\n"
" src_port PORT-NUMBER |\n"
+ " tcp_flags MASKED-TCP_FLAGS |\n"
" type MASKED-ICMP-TYPE |\n"
" code MASKED-ICMP-CODE |\n"
" arp_tip IPV4-PREFIX |\n"
return 0;
}
+#define TCP_FLAGS_MAX_MASK 0xfff
+
+static int flower_parse_tcp_flags(char *str, int flags_type, int mask_type,
+ struct nlmsghdr *n)
+{
+ char *slash;
+ int ret, err = -1;
+ __u16 flags;
+
+ slash = strchr(str, '/');
+ if (slash)
+ *slash = '\0';
+
+ ret = get_u16(&flags, str, 16);
+ if (ret < 0 || flags & ~TCP_FLAGS_MAX_MASK)
+ goto err;
+
+ addattr16(n, MAX_MSG, flags_type, htons(flags));
+
+ if (slash) {
+ ret = get_u16(&flags, slash + 1, 16);
+ if (ret < 0 || flags & ~TCP_FLAGS_MAX_MASK)
+ goto err;
+ } else {
+ flags = TCP_FLAGS_MAX_MASK;
+ }
+ addattr16(n, MAX_MSG, mask_type, htons(flags));
+
+ err = 0;
+err:
+ if (slash)
+ *slash = '/';
+ return err;
+}
+
+static int flower_parse_ip_tos_ttl(char *str, int key_type, int mask_type,
+ struct nlmsghdr *n)
+{
+ char *slash;
+ int ret, err = -1;
+ __u8 tos_ttl;
+
+ slash = strchr(str, '/');
+ if (slash)
+ *slash = '\0';
+
+ ret = get_u8(&tos_ttl, str, 10);
+ if (ret < 0)
+ ret = get_u8(&tos_ttl, str, 16);
+ if (ret < 0)
+ goto err;
+
+ addattr8(n, MAX_MSG, key_type, tos_ttl);
+
+ if (slash) {
+ ret = get_u8(&tos_ttl, slash + 1, 16);
+ if (ret < 0)
+ goto err;
+ } else {
+ tos_ttl = 0xff;
+ }
+ addattr8(n, MAX_MSG, mask_type, tos_ttl);
+
+ err = 0;
+err:
+ if (slash)
+ *slash = '/';
+ return err;
+}
+
static int flower_parse_key_id(const char *str, int type, struct nlmsghdr *n)
{
int ret;
fprintf(stderr, "Illegal \"ip_proto\"\n");
return -1;
}
+ } else if (matches(*argv, "ip_tos") == 0) {
+ NEXT_ARG();
+ ret = flower_parse_ip_tos_ttl(*argv,
+ TCA_FLOWER_KEY_IP_TOS,
+ TCA_FLOWER_KEY_IP_TOS_MASK,
+ n);
+ if (ret < 0) {
+ fprintf(stderr, "Illegal \"ip_tos\"\n");
+ return -1;
+ }
+ } else if (matches(*argv, "ip_ttl") == 0) {
+ NEXT_ARG();
+ ret = flower_parse_ip_tos_ttl(*argv,
+ TCA_FLOWER_KEY_IP_TTL,
+ TCA_FLOWER_KEY_IP_TTL_MASK,
+ n);
+ if (ret < 0) {
+ fprintf(stderr, "Illegal \"ip_ttl\"\n");
+ return -1;
+ }
} else if (matches(*argv, "dst_ip") == 0) {
NEXT_ARG();
ret = flower_parse_ip_addr(*argv, vlan_ethtype ?
fprintf(stderr, "Illegal \"src_port\"\n");
return -1;
}
+ } else if (matches(*argv, "tcp_flags") == 0) {
+ NEXT_ARG();
+ ret = flower_parse_tcp_flags(*argv,
+ TCA_FLOWER_KEY_TCP_FLAGS,
+ TCA_FLOWER_KEY_TCP_FLAGS_MASK,
+ n);
+ if (ret < 0) {
+ fprintf(stderr, "Illegal \"tcp_flags\"\n");
+ return -1;
+ }
} else if (matches(*argv, "type") == 0) {
NEXT_ARG();
ret = flower_parse_icmp(*argv, eth_type, ip_proto,
*p_ip_proto = ip_proto;
}
+static void flower_print_ip_attr(FILE *f, char *name,
+ struct rtattr *key_attr,
+ struct rtattr *mask_attr)
+{
+ if (!key_attr)
+ return;
+
+ fprintf(f, "\n %s %x", name, rta_getattr_u8(key_attr));
+ if (!mask_attr)
+ return;
+ fprintf(f, "/%x", rta_getattr_u8(mask_attr));
+}
+
static void flower_print_matching_flags(FILE *f, char *name,
enum flower_matching_flags type,
struct rtattr *attr,
fprintf(f, "\n %s %d", name, rta_getattr_be16(attr));
}
+static void flower_print_tcp_flags(FILE *f, char *name,
+ struct rtattr *flags_attr,
+ struct rtattr *mask_attr)
+{
+ if (!flags_attr)
+ return;
+ fprintf(f, "\n %s %x", name, rta_getattr_be16(flags_attr));
+ if (!mask_attr)
+ return;
+ fprintf(f, "/%x", rta_getattr_be16(mask_attr));
+}
+
+
static void flower_print_key_id(FILE *f, const char *name,
struct rtattr *attr)
{
flower_print_eth_type(f, ð_type, tb[TCA_FLOWER_KEY_ETH_TYPE]);
flower_print_ip_proto(f, &ip_proto, tb[TCA_FLOWER_KEY_IP_PROTO]);
+ flower_print_ip_attr(f, "ip_tos", tb[TCA_FLOWER_KEY_IP_TOS],
+ tb[TCA_FLOWER_KEY_IP_TOS_MASK]);
+ flower_print_ip_attr(f, "ip_ttl", tb[TCA_FLOWER_KEY_IP_TTL],
+ tb[TCA_FLOWER_KEY_IP_TTL_MASK]);
+
flower_print_ip_addr(f, "dst_ip", eth_type,
tb[TCA_FLOWER_KEY_IPV4_DST],
tb[TCA_FLOWER_KEY_IPV4_DST_MASK],
if (nl_type >= 0)
flower_print_port(f, "src_port", tb[nl_type]);
+ flower_print_tcp_flags(f, "tcp_flags", tb[TCA_FLOWER_KEY_TCP_FLAGS],
+ tb[TCA_FLOWER_KEY_TCP_FLAGS_MASK]);
+
nl_type = flower_icmp_attr_type(eth_type, ip_proto,
FLOWER_ICMP_FIELD_TYPE);
nl_mask_type = flower_icmp_attr_mask_type(eth_type, ip_proto,
int tca_id, struct nlmsghdr *n)
{
const char *bpf_obj = NULL, *bpf_uds_name = NULL;
- struct tc_act_bpf parm = { .action = TC_ACT_PIPE };
+ struct tc_act_bpf parm = {};
struct bpf_cfg_in cfg = {};
bool seen_run = false;
struct rtattr *tail;
NEXT_ARG_FWD();
}
- if (argc && !action_a2n(*argv, &parm.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &parm.action,
+ false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
b, sizeof(b)));
}
- fprintf(f, "default-action %s\n", action_n2a(parm->action));
+ print_action_control(f, "default-action ", parm->action, "\n");
fprintf(f, "\tindex %u ref %d bind %d", parm->index, parm->refcnt,
parm->bindcnt);
fprintf(stderr, "Usage: ... connmark [zone ZONE] [CONTROL] [index <INDEX>]\n");
fprintf(stderr, "where :\n"
"\tZONE is the conntrack zone\n"
- "\tCONTROL := reclassify|pipe|drop|continue|ok\n");
+ "\tCONTROL := reclassify | pipe | drop | continue | ok |\n"
+ "\t goto chain <CHAIN_INDEX>\n");
}
static void
}
}
- sel.action = TC_ACT_PIPE;
- if (argc && !action_a2n(*argv, &sel.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
return -1;
}
- if (argc && !action_a2n(*argv, &sel.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK);
if (argc) {
if (matches(*argv, "index") == 0) {
uflag_1 = "?empty";
}
- fprintf(f, "csum (%s%s%s%s%s%s%s) action %s\n",
+ fprintf(f, "csum (%s%s%s%s%s%s%s) ",
uflag_1, uflag_2, uflag_3,
- uflag_4, uflag_5, uflag_6, uflag_7,
- action_n2a(sel->action));
+ uflag_4, uflag_5, uflag_6, uflag_7);
+ print_action_control(f, "action ", sel->action, "\n");
fprintf(f, "\tindex %u ref %d bind %d", sel->index, sel->refcnt,
sel->bindcnt);
#ifdef CONFIG_GACT_PROB
fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [INDEX]\n");
fprintf(stderr,
- "Where: \tACTION := reclassify | drop | continue | pass | pipe\n"
+ "Where: \tACTION := reclassify | drop | continue | pass | pipe |\n"
+ " \t goto chain <CHAIN_INDEX>\n"
"\tRAND := random <RANDTYPE> <ACTION> <VAL>\n"
"\tRANDTYPE := netrand | determ\n"
"\tVAL : = value not exceeding 10000\n"
#else
fprintf(stderr, "Usage: ... gact <ACTION> [INDEX]\n");
fprintf(stderr,
- "Where: \tACTION := reclassify | drop | continue | pass | pipe\n"
+ "Where: \tACTION := reclassify | drop | continue | pass | pipe |\n"
+ " \t goto chain <CHAIN_INDEX>\n"
"\tINDEX := index value used\n"
"\n");
#endif
exit(-1);
}
-static int
-get_act(char ***argv_p)
-{
- int n;
-
- if (action_a2n(**argv_p, &n, false)) {
- fprintf(stderr, "bad action type %s\n", **argv_p);
- return -10;
- }
- return n;
-}
-
static int
parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n)
int argc = *argc_p;
char **argv = *argv_p;
int ok = 0;
- int action = TC_POLICE_RECLASSIFY;
- struct tc_gact p = { .action = TC_POLICE_RECLASSIFY };
+ struct tc_gact p = { 0 };
#ifdef CONFIG_GACT_PROB
int rd = 0;
struct tc_gact_p pp;
if (matches(*argv, "gact") == 0) {
ok++;
- } else {
- action = get_act(&argv);
- if (action != -10) {
- p.action = action;
- ok++;
- } else {
- explain();
- return action;
- }
- }
-
- if (ok) {
argc--;
argv++;
+ } else {
+ if (parse_action_control(&argc, &argv, &p.action, false) == -1)
+ usage();
+ ok++;
}
#ifdef CONFIG_GACT_PROB
return -1;
}
- action = get_act(&argv);
- if (action != -10) { /* FIXME */
- pp.paction = action;
- } else {
- explain();
- return -1;
- }
- argc--;
- argv++;
+ if (parse_action_control(&argc, &argv,
+ &pp.paction, false) == -1)
+ usage();
if (get_u16(&pp.pval, *argv, 10)) {
fprintf(stderr, "Illegal probability val 0x%x\n", pp.pval);
return -1;
}
p = RTA_DATA(tb[TCA_GACT_PARMS]);
- fprintf(f, "gact action %s", action_n2a(p->action));
+ fprintf(f, "gact ");
+ print_action_control(f, "action ", p->action, "");
#ifdef CONFIG_GACT_PROB
if (tb[TCA_GACT_PROB] != NULL) {
pp = RTA_DATA(tb[TCA_GACT_PROB]);
memset(&pp_dummy, 0, sizeof(pp_dummy));
pp = &pp_dummy;
}
- fprintf(f, "\n\t random type %s %s val %d",
- prob_n2a(pp->ptype), action_n2a(pp->paction), pp->pval);
+ fprintf(f, "\n\t random type %s", prob_n2a(pp->ptype));
+ print_action_control(f, " ", pp->paction, " ");
+ fprintf(f, "val %d", pp->pval);
#endif
fprintf(f, "\n\t index %u ref %d bind %d", p->index, p->refcnt,
p->bindcnt);
int argc = *argc_p;
char **argv = *argv_p;
int ok = 0;
- struct tc_ife p = { .action = TC_ACT_PIPE }; /* good default */
+ struct tc_ife p = { 0 };
struct rtattr *tail;
struct rtattr *tail2;
char dbuf[ETH_ALEN];
argv++;
}
- if (argc && !action_a2n(*argv, &p.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
}
p = RTA_DATA(tb[TCA_IFE_PARMS]);
- fprintf(f, "ife %s action %s ",
- (p->flags & IFE_ENCODE) ? "encode" : "decode",
- action_n2a(p->action));
+ fprintf(f, "ife %s ", p->flags & IFE_ENCODE ? "encode" : "decode");
+ print_action_control(f, "action ", p->action, " ");
if (tb[TCA_IFE_TYPE]) {
ife_type = rta_getattr_u16(tb[TCA_IFE_TYPE]);
}
- if (argc &&
- (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
- && !action_a2n(*argv, &p.action, false))
- NEXT_ARG();
+ if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
+ parse_action_control(&argc, &argv, &p.action, false);
if (argc) {
if (iok && matches(*argv, "index") == 0) {
return -1;
}
- fprintf(f, "mirred (%s to device %s) %s",
- mirred_n2a(p->eaction), dev, action_n2a(p->action));
+ fprintf(f, "mirred (%s to device %s)", mirred_n2a(p->eaction), dev);
+ print_action_control(f, " ", p->action, "");
fprintf(f, "\n ");
fprintf(f, "\tindex %u ref %d bind %d", p->index, p->refcnt,
return -1;
}
- if (argc && !action_a2n(*argv, &sel.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK);
if (argc) {
if (matches(*argv, "index") == 0) {
len = ffs(sel->mask);
len = len ? 33 - len : 0;
- fprintf(f, " nat %s %s/%d %s %s", sel->flags & TCA_NAT_FLAG_EGRESS ?
- "egress" : "ingress",
+ fprintf(f, " nat %s %s/%d %s", sel->flags & TCA_NAT_FLAG_EGRESS ?
+ "egress" : "ingress",
format_host_r(AF_INET, 4, &sel->old_addr, buf1, sizeof(buf1)),
len,
- format_host_r(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)),
- action_n2a(sel->action));
+ format_host_r(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)));
+ print_action_control(f, " ", sel->action, "");
if (show_stats) {
if (tb[TCA_NAT_TM]) {
"\t\tCMD:= clear | invert | set <setval>| add <addval> | retain\n"
"\t<LAYERED>:= ip <ipdata> | ip6 <ip6data>\n"
" \t\t| udp <udpdata> | tcp <tcpdata> | icmp <icmpdata>\n"
- "\tCONTROL:= reclassify | pipe | drop | continue | pass\n"
+ "\tCONTROL:= reclassify | pipe | drop | continue | pass |\n"
+ "\t goto chain <CHAIN_INDEX>\n"
"\tNOTE: if 'ex' is set, extended functionality will be supported (kernel >= 4.11)\n"
"For Example usage look at the examples directory\n");
return -1;
}
- if (argc && !action_a2n(*argv, &sel.sel.action, false))
- NEXT_ARG();
+ parse_action_control_dflt(&argc, &argv, &sel.sel.action, false, TC_ACT_OK);
if (argc) {
if (matches(*argv, "index") == 0) {
}
}
- fprintf(f, " pedit action %s keys %d\n ",
- action_n2a(sel->action), sel->nkeys);
+ fprintf(f, " pedit ");
+ print_action_control(f, "action ", sel->action, " ");
+ fprintf(f,"keys %d\n ", sel->nkeys);
fprintf(f, "\t index %u ref %d bind %d", sel->index, sel->refcnt,
sel->bindcnt);
fprintf(stderr, "Where: CONTROL := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT]\n");
fprintf(stderr, " Define how to handle packets which exceed (<EXCEEDACT>)\n");
fprintf(stderr, " or conform (<NOTEXCEEDACT>) the configured bandwidth limit.\n");
- fprintf(stderr, " EXCEEDACT/NOTEXCEEDACT := { pipe | ok | reclassify | drop | continue }\n");
+ fprintf(stderr, " EXCEEDACT/NOTEXCEEDACT := { pipe | ok | reclassify | drop | continue |\n");
+ fprintf(stderr, " goto chain <CHAIN_INDEX> }\n");
exit(-1);
}
fprintf(stderr, "Illegal \"%s\"\n", arg);
}
-static int get_police_result(int *action, int *result, char *arg)
-{
- char *p = strchr(arg, '/');
-
- if (p)
- *p = 0;
-
- if (action_a2n(arg, action, true)) {
- if (p)
- *p = '/';
- return -1;
- }
-
- if (p) {
- *p = '/';
- if (action_a2n(p+1, result, true))
- return -1;
- }
- return 0;
-}
-
int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n)
{
explain1("peakrate");
return -1;
}
- } else if (matches(*argv, "reclassify") == 0) {
- p.action = TC_POLICE_RECLASSIFY;
- } else if (matches(*argv, "drop") == 0 ||
- matches(*argv, "shot") == 0) {
- p.action = TC_POLICE_SHOT;
- } else if (matches(*argv, "continue") == 0) {
- p.action = TC_POLICE_UNSPEC;
- } else if (matches(*argv, "pass") == 0) {
- p.action = TC_POLICE_OK;
- } else if (matches(*argv, "pipe") == 0) {
- p.action = TC_POLICE_PIPE;
+ } else if (matches(*argv, "reclassify") == 0 ||
+ matches(*argv, "drop") == 0 ||
+ matches(*argv, "shot") == 0 ||
+ matches(*argv, "continue") == 0 ||
+ matches(*argv, "pass") == 0 ||
+ matches(*argv, "pipe") == 0 ||
+ matches(*argv, "goto") == 0) {
+ if (parse_action_control(&argc, &argv, &p.action, false))
+ return -1;
} else if (strcmp(*argv, "conform-exceed") == 0) {
NEXT_ARG();
- if (get_police_result(&p.action, &presult, *argv)) {
- fprintf(stderr, "Illegal \"action\"\n");
+ if (parse_action_control_slash(&argc, &argv, &p.action,
+ &presult, true))
return -1;
- }
} else if (matches(*argv, "overhead") == 0) {
NEXT_ARG();
if (get_u16(&overhead, *argv, 10)) {
fprintf(f, "avrate %s ",
sprint_rate(rta_getattr_u32(tb[TCA_POLICE_AVRATE]),
b1));
- fprintf(f, "action %s", action_n2a(p->action));
+
+ print_action_control(f, "action ", p->action, "");
if (tb[TCA_POLICE_RESULT]) {
__u32 action = rta_getattr_u32(tb[TCA_POLICE_RESULT]);
- fprintf(f, "/%s ", action_n2a(action));
+ print_action_control(f, "/", action, " ");
} else
fprintf(f, " ");
NEXT_ARG_FWD();
}
- p.action = TC_ACT_PIPE;
- if (argc && !action_a2n(*argv, &p.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
}
}
- if (argc && !action_a2n(*argv, &sel.action, false))
- NEXT_ARG_FWD();
-
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
argv++;
}
- sel.action = TC_ACT_PIPE;
- if (argc && !action_a2n(*argv, &sel.action, false))
- NEXT_ARG();
+ parse_action_control_dflt(&argc, &argv, &sel.action,
+ false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
fprintf(f, " ptype %d", *ptype);
}
- fprintf(f, " %s", action_n2a(p->action));
+ print_action_control(f, " ", p->action, "");
fprintf(f, "\n\t index %u ref %d bind %d",
p->index, p->refcnt, p->bindcnt);
"\tDMAC := 6 byte Destination MAC address\n"
"\tSMAC := optional 6 byte Source MAC address\n"
"\tETYPE := optional 16 bit ethertype\n"
- "\tCONTROL := reclassify|pipe|drop|continue|ok\n"
+ "\tCONTROL := reclassify | pipe | drop | continue | ok |\n"
+ "\t goto chain <CHAIN_INDEX>\n"
"\tINDEX := skbmod index value to use\n");
}
char *saddr = NULL;
memset(&p, 0, sizeof(p));
- p.action = TC_ACT_PIPE; /* good default */
if (argc <= 0)
return -1;
argv++;
}
- if (argc) {
- if (matches(*argv, "reclassify") == 0) {
- p.action = TC_ACT_RECLASSIFY;
- argc--;
- argv++;
- } else if (matches(*argv, "pipe") == 0) {
- p.action = TC_ACT_PIPE;
- argc--;
- argv++;
- } else if (matches(*argv, "drop") == 0 ||
- matches(*argv, "shot") == 0) {
- p.action = TC_ACT_SHOT;
- argc--;
- argv++;
- } else if (matches(*argv, "continue") == 0) {
- p.action = TC_ACT_UNSPEC;
- argc--;
- argv++;
- } else if (matches(*argv, "pass") == 0 ||
- matches(*argv, "ok") == 0) {
- p.action = TC_ACT_OK;
- argc--;
- argv++;
- }
- }
+ parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
p = RTA_DATA(tb[TCA_SKBMOD_PARMS]);
- fprintf(f, "skbmod action %s ", action_n2a(p->action));
+ fprintf(f, "skbmod ");
+ print_action_control(f, "", p->action, " ");
if (tb[TCA_SKBMOD_ETYPE]) {
skbmod_etype = rta_getattr_u16(tb[TCA_SKBMOD_ETYPE]);
static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n)
{
- struct tc_tunnel_key parm = { .action = TC_ACT_PIPE };
+ struct tc_tunnel_key parm = {};
char **argv = *argv_p;
int argc = *argc_p;
struct rtattr *tail;
NEXT_ARG_FWD();
}
- if (argc && !action_a2n(*argv, &parm.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &parm.action,
+ false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
break;
}
- fprintf(f, " %s", action_n2a(parm->action));
+ print_action_control(f, " ", parm->action, "");
fprintf(f, "\n\tindex %d ref %d bind %d", parm->index, parm->refcnt,
parm->bindcnt);
fprintf(stderr, " vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n");
fprintf(stderr, " VLANPROTO is one of 802.1Q or 802.1AD\n");
fprintf(stderr, " with default: 802.1Q\n");
- fprintf(stderr, " CONTROL := reclassify | pipe | drop | continue | pass\n");
+ fprintf(stderr, " CONTROL := reclassify | pipe | drop | continue | pass |\n");
+ fprintf(stderr, " goto chain <CHAIN_INDEX>\n");
}
static void usage(void)
int proto_set = 0;
__u8 prio;
int prio_set = 0;
- struct tc_vlan parm = { 0 };
+ struct tc_vlan parm = {};
if (matches(*argv, "vlan") != 0)
return -1;
argv++;
}
- parm.action = TC_ACT_PIPE;
- if (argc && !action_a2n(*argv, &parm.action, false))
- NEXT_ARG_FWD();
+ parse_action_control_dflt(&argc, &argv, &parm.action,
+ false, TC_ACT_PIPE);
if (argc) {
if (matches(*argv, "index") == 0) {
}
break;
}
- fprintf(f, " %s", action_n2a(parm->action));
+ print_action_control(f, " ", parm->action, "");
fprintf(f, "\n\t index %u ref %d bind %d", parm->index, parm->refcnt,
parm->bindcnt);
fprintf(stderr,
"Usage: tc filter [ add | del | change | replace | show ] dev STRING\n"
"Usage: tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
- " [ pref PRIO ] protocol PROTO\n"
+ " [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]\n"
" [ estimator INTERVAL TIME_CONSTANT ]\n"
" [ root | ingress | egress | parent CLASSID ]\n"
" [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"
__u32 prio = 0;
__u32 protocol = 0;
int protocol_set = 0;
+ __u32 chain_index;
+ int chain_index_set = 0;
char *fhandle = NULL;
char d[16] = {};
char k[16] = {};
invarg("invalid protocol", *argv);
protocol = id;
protocol_set = 1;
+ } else if (matches(*argv, "chain") == 0) {
+ NEXT_ARG();
+ if (chain_index_set)
+ duparg("chain", *argv);
+ if (get_u32(&chain_index, *argv, 0))
+ invarg("invalid chain index value", *argv);
+ chain_index_set = 1;
} else if (matches(*argv, "estimator") == 0) {
if (parse_estimator(&argc, &argv, &est) < 0)
return -1;
req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+ if (chain_index_set)
+ addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
+
if (k[0])
addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
return -1;
}
}
+
if (est.ewma_log)
addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
static int filter_ifindex;
static __u32 filter_prio;
static __u32 filter_protocol;
+static __u32 filter_chain_index;
+static int filter_chain_index_set;
__u16 f_proto;
int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
}
}
fprintf(fp, "%s ", rta_getattr_str(tb[TCA_KIND]));
+
+ if (tb[TCA_CHAIN]) {
+ __u32 chain_index = rta_getattr_u32(tb[TCA_CHAIN]);
+
+ if (!filter_chain_index_set ||
+ filter_chain_index != chain_index)
+ fprintf(fp, "chain %u ", chain_index);
+ }
+
q = get_filter_kind(RTA_DATA(tb[TCA_KIND]));
if (tb[TCA_OPTIONS]) {
if (q)
__u32 prio = 0;
__u32 protocol = 0;
int protocol_set = 0;
+ __u32 chain_index;
+ int chain_index_set = 0;
__u32 parent_handle = 0;
char *fhandle = NULL;
char d[16] = {};
invarg("invalid protocol", *argv);
protocol = id;
protocol_set = 1;
+ } else if (matches(*argv, "chain") == 0) {
+ NEXT_ARG();
+ if (chain_index_set)
+ duparg("chain", *argv);
+ if (get_u32(&chain_index, *argv, 0))
+ invarg("invalid chain index value", *argv);
+ chain_index_set = 1;
} else if (matches(*argv, "help") == 0) {
usage();
return 0;
req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+ if (chain_index_set)
+ addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
+
if (req.t.tcm_parent == TC_H_UNSPEC) {
fprintf(stderr, "Must specify filter parent\n");
return -1;
static int tc_filter_list(int argc, char **argv)
{
- struct tcmsg t = { .tcm_family = AF_UNSPEC };
+ struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[MAX_MSG];
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
+ .n.nlmsg_type = RTM_GETTFILTER,
+ .t.tcm_parent = TC_H_UNSPEC,
+ .t.tcm_family = AF_UNSPEC,
+ };
char d[16] = {};
__u32 prio = 0;
__u32 protocol = 0;
+ __u32 chain_index;
char *fhandle = NULL;
while (argc > 0) {
duparg("dev", *argv);
strncpy(d, *argv, sizeof(d)-1);
} else if (strcmp(*argv, "root") == 0) {
- if (t.tcm_parent) {
+ if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"root\" is duplicate parent ID\n");
return -1;
}
- filter_parent = t.tcm_parent = TC_H_ROOT;
+ filter_parent = req.t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "ingress") == 0) {
- if (t.tcm_parent) {
+ if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"ingress\" is duplicate parent ID\n");
return -1;
}
filter_parent = TC_H_MAKE(TC_H_CLSACT,
TC_H_MIN_INGRESS);
- t.tcm_parent = filter_parent;
+ req.t.tcm_parent = filter_parent;
} else if (strcmp(*argv, "egress") == 0) {
- if (t.tcm_parent) {
+ if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"egress\" is duplicate parent ID\n");
return -1;
}
filter_parent = TC_H_MAKE(TC_H_CLSACT,
TC_H_MIN_EGRESS);
- t.tcm_parent = filter_parent;
+ req.t.tcm_parent = filter_parent;
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
NEXT_ARG();
- if (t.tcm_parent)
+ if (req.t.tcm_parent)
duparg("parent", *argv);
if (get_tc_classid(&handle, *argv))
invarg("invalid parent ID", *argv);
- filter_parent = t.tcm_parent = handle;
+ filter_parent = req.t.tcm_parent = handle;
} else if (strcmp(*argv, "handle") == 0) {
NEXT_ARG();
if (fhandle)
invarg("invalid protocol", *argv);
protocol = res;
filter_protocol = protocol;
+ } else if (matches(*argv, "chain") == 0) {
+ NEXT_ARG();
+ if (filter_chain_index_set)
+ duparg("chain", *argv);
+ if (get_u32(&chain_index, *argv, 0))
+ invarg("invalid chain index value", *argv);
+ filter_chain_index_set = 1;
+ filter_chain_index = chain_index;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
argc--; argv++;
}
- t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+ req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
ll_init_map(&rth);
if (d[0]) {
- t.tcm_ifindex = ll_name_to_index(d);
- if (t.tcm_ifindex == 0) {
+ req.t.tcm_ifindex = ll_name_to_index(d);
+ if (req.t.tcm_ifindex == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
return 1;
}
- filter_ifindex = t.tcm_ifindex;
+ filter_ifindex = req.t.tcm_ifindex;
}
- if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) {
+ if (filter_chain_index_set)
+ addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
+
+ if (rtnl_dump_request_n(&rth, &req.n) < 0) {
perror("Cannot send dump request");
return 1;
}
return buf;
}
-const char *action_n2a(int action)
+static const char *action_n2a(int action)
{
static char buf[64];
+ if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
+ return "goto";
switch (action) {
case TC_ACT_UNSPEC:
return "continue";
return "pipe";
case TC_ACT_STOLEN:
return "stolen";
+ case TC_ACT_TRAP:
+ return "trap";
default:
snprintf(buf, 64, "%d", action);
buf[63] = '\0';
*
* In error case, returns -1 and does not touch @result. Otherwise returns 0.
*/
-int action_a2n(char *arg, int *result, bool allow_num)
+static int action_a2n(char *arg, int *result, bool allow_num)
{
int n;
char dummy;
{"ok", TC_ACT_OK},
{"reclassify", TC_ACT_RECLASSIFY},
{"pipe", TC_ACT_PIPE},
+ {"goto", TC_ACT_GOTO_CHAIN},
+ {"trap", TC_ACT_TRAP},
{ NULL },
}, *iter;
return 0;
}
+/* Parse action control including possible options.
+ *
+ * Parameters:
+ * @argc_p - pointer to argc to parse
+ * @argv_p - pointer to argv to parse
+ * @result_p - pointer to output variable
+ * @allow_num - whether action may be in numeric format already
+ *
+ * In error case, returns -1 and does not touch @result_1p. Otherwise returns 0.
+ */
+int parse_action_control(int *argc_p, char ***argv_p,
+ int *result_p, bool allow_num)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int result;
+
+ if (!argc)
+ return -1;
+ if (action_a2n(*argv, &result, allow_num) == -1) {
+ fprintf(stderr, "Bad action type %s\n", *argv);
+ return -1;
+ }
+ if (result == TC_ACT_GOTO_CHAIN) {
+ __u32 chain_index;
+
+ NEXT_ARG();
+ if (matches(*argv, "chain") != 0) {
+ fprintf(stderr, "\"chain index\" expected\n");
+ return -1;
+ }
+ NEXT_ARG();
+ if (get_u32(&chain_index, *argv, 10) ||
+ chain_index > TC_ACT_EXT_VAL_MASK) {
+ fprintf(stderr, "Illegal \"chain index\"\n");
+ return -1;
+ }
+ result |= chain_index;
+ }
+ NEXT_ARG_FWD();
+ *argc_p = argc;
+ *argv_p = argv;
+ *result_p = result;
+ return 0;
+}
+
+/* Parse action control including possible options.
+ *
+ * Parameters:
+ * @argc_p - pointer to argc to parse
+ * @argv_p - pointer to argv to parse
+ * @result_p - pointer to output variable
+ * @allow_num - whether action may be in numeric format already
+ * @default_result - set as a result in case of parsing error
+ *
+ * In case there is an error during parsing, the default result is used.
+ */
+void parse_action_control_dflt(int *argc_p, char ***argv_p,
+ int *result_p, bool allow_num,
+ int default_result)
+{
+ if (parse_action_control(argc_p, argv_p, result_p, allow_num))
+ *result_p = default_result;
+}
+
+static int parse_action_control_slash_spaces(int *argc_p, char ***argv_p,
+ int *result1_p, int *result2_p,
+ bool allow_num)
+{
+ int argc = *argc_p;
+ char **argv = *argv_p;
+ int result1, result2;
+ int *result_p = &result1;
+ int ok = 0;
+ int ret;
+
+ while (argc > 0) {
+ switch (ok) {
+ case 1:
+ if (strcmp(*argv, "/") != 0)
+ goto out;
+ result_p = &result2;
+ NEXT_ARG();
+ /* fall-through */
+ case 0: /* fall-through */
+ case 2:
+ ret = parse_action_control(&argc, &argv,
+ result_p, allow_num);
+ if (ret)
+ return ret;
+ ok++;
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ *result1_p = result1;
+ if (ok == 2)
+ *result2_p = result2;
+ *argc_p = argc;
+ *argv_p = argv;
+ return 0;
+}
+
+/* Parse action control with slash including possible options.
+ *
+ * Parameters:
+ * @argc_p - pointer to argc to parse
+ * @argv_p - pointer to argv to parse
+ * @result1_p - pointer to the first (before slash) output variable
+ * @result2_p - pointer to the second (after slash) output variable
+ * @allow_num - whether action may be in numeric format already
+ *
+ * In error case, returns -1 and does not touch @result*. Otherwise returns 0.
+ */
+int parse_action_control_slash(int *argc_p, char ***argv_p,
+ int *result1_p, int *result2_p, bool allow_num)
+{
+ char **argv = *argv_p;
+ int result1, result2;
+ char *p = strchr(*argv, '/');
+
+ if (!p)
+ return parse_action_control_slash_spaces(argc_p, argv_p,
+ result1_p, result2_p,
+ allow_num);
+ *p = 0;
+ if (action_a2n(*argv, &result1, allow_num)) {
+ if (p)
+ *p = '/';
+ return -1;
+ }
+
+ *p = '/';
+ if (action_a2n(p + 1, &result2, allow_num))
+ return -1;
+
+ *result1_p = result1;
+ *result2_p = result2;
+ return 0;
+}
+
+void print_action_control(FILE *f, const char *prefix,
+ int action, const char *suffix)
+{
+ fprintf(f, "%s%s", prefix, action_n2a(action));
+ if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
+ fprintf(f, " chain %u", action & TC_ACT_EXT_VAL_MASK);
+ fprintf(f, "%s", suffix);
+}
+
int get_linklayer(unsigned int *val, const char *arg)
{
int res;
int tc_print_police(FILE *f, struct rtattr *tb);
int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n);
-const char *action_n2a(int action);
-int action_a2n(char *arg, int *result, bool allow_num);
+int parse_action_control(int *argc_p, char ***argv_p,
+ int *result_p, bool allow_num);
+void parse_action_control_dflt(int *argc_p, char ***argv_p,
+ int *result_p, bool allow_num,
+ int default_result);
+int parse_action_control_slash(int *argc_p, char ***argv_p,
+ int *result1_p, int *result2_p, bool allow_num);
+void print_action_control(FILE *f, const char *prefix,
+ int action, const char *suffix);
int act_parse_police(struct action_util *a, int *argc_p,
char ***argv_p, int tca_id, struct nlmsghdr *n);
int print_police(struct action_util *a, FILE *f, struct rtattr *tb);