]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge branch 'master' into net-next
authorStephen Hemminger <stephen@networkplumber.org>
Thu, 15 Jun 2017 15:32:53 +0000 (08:32 -0700)
committerStephen Hemminger <stephen@networkplumber.org>
Thu, 15 Jun 2017 15:32:53 +0000 (08:32 -0700)
33 files changed:
bridge/fdb.c
include/linux/bpf.h
include/linux/if_link.h
include/linux/neighbour.h
include/linux/pkt_cls.h
include/linux/rtnetlink.h
ip/ipaddress.c
ip/iproute.c
man/man8/ip-route.8.in
man/man8/tc-flower.8
man/man8/tc-ife.8
man/man8/tc-pedit.8
man/man8/tc-police.8
man/man8/tc-vlan.8
tc/f_flower.c
tc/m_bpf.c
tc/m_connmark.c
tc/m_csum.c
tc/m_gact.c
tc/m_ife.c
tc/m_mirred.c
tc/m_nat.c
tc/m_pedit.c
tc/m_police.c
tc/m_sample.c
tc/m_simple.c
tc/m_skbedit.c
tc/m_skbmod.c
tc/m_tunnel_key.c
tc/m_vlan.c
tc/tc_filter.c
tc/tc_util.c
tc/tc_util.h

index a71a78f23b202e8e3fd8742345a6c8c3ecefca4e..e5cebf9ba69c1807bfc21578142b5b00f9af825d 100644 (file)
@@ -261,6 +261,10 @@ int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                        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");
                }
@@ -280,6 +284,8 @@ int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                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 ",
index d322ca15199dcef7bc23693788a98cd3dadea372..8a966ef03b1d174db0a2bfed256efe56d5a54e28 100644 (file)
@@ -82,6 +82,11 @@ enum bpf_cmd {
        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 {
@@ -209,6 +214,21 @@ union bpf_attr {
                __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:
@@ -313,8 +333,11 @@ union bpf_attr {
  *     @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
@@ -328,11 +351,11 @@ union bpf_attr {
  *     @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
@@ -490,6 +513,11 @@ union bpf_attr {
  *     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),                     \
@@ -539,7 +567,8 @@ union bpf_attr {
        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
@@ -670,4 +699,25 @@ struct xdp_md {
        __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__ */
index 5a3a048e8f242dd3d89ef0c12fdcd8d3cad98c13..a536a24b1c288ac30be5c386b264620437722940 100644 (file)
@@ -157,6 +157,7 @@ enum {
        IFLA_GSO_MAX_SIZE,
        IFLA_PAD,
        IFLA_XDP,
+       IFLA_EVENT,
        __IFLA_MAX
 };
 
@@ -909,4 +910,14 @@ enum {
 
 #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 */
index f3d16dbe09d64424d2d92c581f30771f6add6e0b..3199d28980b35442021ed1141151ab957b41c9f9 100644 (file)
@@ -41,6 +41,7 @@ enum {
 #define NTF_MASTER     0x04
 #define NTF_PROXY      0x08    /* == ATF_PUBL */
 #define NTF_EXT_LEARNED        0x10
+#define NTF_OFFLOADED   0x20
 #define NTF_ROUTER     0x80
 
 /*
index d613be3b3239e476d728ffa323347ef42e97eaec..2055783e6ee9c33cb8d6ffe12b0f1a61e13a3af3 100644 (file)
@@ -37,6 +37,13 @@ enum {
 #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
@@ -51,6 +58,7 @@ enum {
        (((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 {
@@ -450,6 +458,14 @@ 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,
 };
 
index a96db837e049680582c62b79c01041f44244b38c..6cb64fa75c2be784f4f9e08bfc912ab11b1c663d 100644 (file)
@@ -278,6 +278,7 @@ enum rt_scope_t {
 #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 */
 
@@ -549,6 +550,7 @@ enum {
        TCA_STAB,
        TCA_PAD,
        TCA_DUMP_INVISIBLE,
+       TCA_CHAIN,
        __TCA_MAX
 };
 
index 4900dce09df87cab21e079b1837046908b7f812b..f06f5829fb613700e7fc7d43469bb34c0e967b16 100644 (file)
@@ -737,6 +737,26 @@ int print_linkinfo_brief(const struct sockaddr_nl *who,
        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)
 {
@@ -842,6 +862,9 @@ int print_linkinfo(const struct sockaddr_nl *who,
        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_);
index 4fd36a1e5b74d126c974518c1191a9abc1755e4d..4e022d77275f6b8e8585fff45ec4ad44b2ae293b 100644 (file)
@@ -65,7 +65,8 @@ static void usage(void)
        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");
@@ -103,6 +104,7 @@ static void usage(void)
        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);
 }
 
@@ -1672,6 +1674,7 @@ static int iproute_get(int argc, char **argv)
        char  *idev = NULL;
        char  *odev = NULL;
        int connected = 0;
+       int fib_match = 0;
        int from_ok = 0;
        unsigned int mark = 0;
 
@@ -1726,6 +1729,8 @@ static int iproute_get(int argc, char **argv)
                        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;
 
@@ -1774,6 +1779,8 @@ static int iproute_get(int argc, char **argv)
                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;
index c8eb38a758523e2a6c0ee052cc542caf8505e033..de8d360a628551867a531cc4ab3d8eb32da8e5ed 100644 (file)
@@ -28,6 +28,7 @@ ip-route \- routing table management
 
 .ti -8
 .B  ip route get
+.I ROUTE_GET_FLAGS
 .IR ADDRESS " [ "
 .BI from " ADDRESS " iif " STRING"
 .RB " ] [ " oif
@@ -219,6 +220,12 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]"
 .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.
@@ -929,6 +936,11 @@ get a single route
 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.
index ba290657c22452ede01af0755f183a33cd241c12..be46f0278b4ff2918681a49670cf06b0dd81e649 100644 (file)
@@ -30,11 +30,17 @@ flower \- flow based traffic control filter
 .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
@@ -120,6 +126,17 @@ may be
 .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"
@@ -136,6 +153,12 @@ Match on layer 4 protocol source or destination port number. Only available for
 .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"
index a8f1f287d1502c2e768a262d7543a7c57b3627c9..24595cc6d615c29f7af49824b781e0bb2bd8aa4a 100644 (file)
@@ -34,7 +34,7 @@ IFE - encapsulate/decapsulate metadata
 
 .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
index 82d4217bc9589169a6f8601b07cd06909e5a4017..bbd725c4d0ba137147b2e44444fd487131d0c96c 100644 (file)
@@ -82,7 +82,7 @@ pedit - generic packet editor action
 
 .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
index 620c28813fc7ea958c68128b953df692a2f0a722..bcc5f438825d1d4a79e4b909f8f402b90fe4d0b2 100644 (file)
@@ -30,7 +30,7 @@ police - policing action
 
 .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
index af3de1c54e343ed4afa3bde075d18733d62c001a..59c81e86378d0b42a37d76d1d0e6fe9900124d26 100644 (file)
@@ -26,7 +26,7 @@ vlan - vlan manipulation module
 
 .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
index ebc63ca6b2a27f6f4893efe32d3b7513b504d6d5..5be693ab7f6afc39b1e30501faa06e10e7a39972 100644 (file)
@@ -53,10 +53,13 @@ static void explain(void)
                "                       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"
@@ -474,6 +477,76 @@ static int flower_parse_port(char *str, __u8 ip_proto,
        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;
@@ -629,6 +702,26 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
                                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 ?
@@ -671,6 +764,16 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
                                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,
@@ -917,6 +1020,19 @@ static void flower_print_ip_proto(FILE *f, __u8 *p_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,
@@ -1000,6 +1116,19 @@ static void flower_print_port(FILE *f, char *name, 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)
 {
@@ -1091,6 +1220,11 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
        flower_print_eth_type(f, &eth_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],
@@ -1110,6 +1244,9 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
        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,
index 1ddc334f2f21b82628ccd7573f4eddc80f87dfc2..57283030a35f59c4c2f76391fe147d6c1a8dd935 100644 (file)
@@ -75,7 +75,7 @@ static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv,
                         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;
@@ -123,8 +123,8 @@ opt_bpf:
                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) {
@@ -186,7 +186,7 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
                                      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);
 
index 295f90d52eefd105f02a076a52c089bd3c9c9202..37d7185415490cb4ecdf8e88eba866066d5e3f6f 100644 (file)
@@ -30,7 +30,8 @@ explain(void)
        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
@@ -80,9 +81,7 @@ parse_connmark(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
                }
        }
 
-       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) {
index 0ee8cad3fbe4cbf4afdfaf4bf08c493145219fbd..7b156734f64c5581fe60fb7fdf012114b15971fd 100644 (file)
@@ -123,8 +123,7 @@ parse_csum(struct action_util *a, int *argc_p,
                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) {
@@ -200,10 +199,10 @@ print_csum(struct action_util *au, FILE *f, struct rtattr *arg)
                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);
 
index 755a3bee2c2f2aa5034f7d1567752b6689c7cadf..1a2583372c34ea30bb074668c025b89a45f1665c 100644 (file)
@@ -45,7 +45,8 @@ explain(void)
 #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"
@@ -54,7 +55,8 @@ explain(void)
 #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
@@ -68,18 +70,6 @@ usage(void)
        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)
@@ -87,8 +77,7 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
        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;
@@ -101,20 +90,12 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
 
        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
@@ -133,15 +114,9 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
                                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;
@@ -212,7 +187,8 @@ print_gact(struct action_util *au, FILE * f, struct rtattr *arg)
        }
        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]);
@@ -221,8 +197,9 @@ print_gact(struct action_util *au, FILE * f, struct rtattr *arg)
                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);
index f6131b1332324741fa705c401246657a568d3ba7..e3521e62c178c7d44bce283352b7d0c6cb7ccf00 100644 (file)
@@ -57,7 +57,7 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p,
        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];
@@ -156,8 +156,7 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p,
                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) {
@@ -245,9 +244,8 @@ static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg)
        }
        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]);
index e9438904fdf50371073e187d73a1f8ec5c085374..2384bda1ff04590045fe4d58635f4a74f3e18f8f 100644 (file)
@@ -170,10 +170,8 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
        }
 
 
-       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) {
@@ -272,8 +270,8 @@ print_mirred(struct action_util *au, FILE * f, struct rtattr *arg)
                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,
index 525f185e2c08274540bccae434595471a963ad0e..31b68fb6bd7845437d251e5a0d15b04b08099ee8 100644 (file)
@@ -115,8 +115,7 @@ parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct
                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) {
@@ -164,12 +163,12 @@ print_nat(struct action_util *au, FILE * f, struct rtattr *arg)
        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]) {
index dfa6b2c4835e9a623a400f75911f81c0cdb0a59f..5d89ab1d832ab92266026027d7992f777d3148f7 100644 (file)
@@ -45,7 +45,8 @@ static void explain(void)
                "\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");
 
@@ -670,8 +671,7 @@ int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
                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) {
@@ -776,8 +776,9 @@ int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
                }
        }
 
-       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);
 
index 226e20e4e8005e954a17a139e0272f4d247e50f2..86117db0482ec93486d4b2f79c09cea8ddca90ae 100644 (file)
@@ -41,7 +41,8 @@ static void usage(void)
        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);
 }
 
@@ -50,27 +51,6 @@ static void explain1(char *arg)
        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)
 {
@@ -166,23 +146,20 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
                                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)) {
@@ -318,12 +295,13 @@ int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
                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, " ");
 
index 9291109071a8935530c0548e05c3aff9e68a3b92..ff5ee6bd1ef63b6f3390811bc5eb7a5f4ce20c0e 100644 (file)
@@ -98,9 +98,7 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p,
                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) {
index 3a8bd916d3bfbae522000a437ef34fe6865a45f3..a4457c70324ee64a8c3a3b34591a200501ab65f5 100644 (file)
@@ -120,9 +120,6 @@ parse_simple(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
                }
        }
 
-       if (argc && !action_a2n(*argv, &sel.action, false))
-               NEXT_ARG_FWD();
-
        if (argc) {
                if (matches(*argv, "index") == 0) {
                        NEXT_ARG();
index 638715f679d379f97745b606c7f0f610522282c4..aa374fcb33ed9b46da3c46cf48eff7a59b56cb5d 100644 (file)
@@ -120,9 +120,8 @@ parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
                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) {
@@ -214,7 +213,7 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
                        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);
index acb7771d2901ba5582e72647de89723fee812f5c..ba79308ba8354cd716d4d097b204d354b46b8fec 100644 (file)
@@ -36,7 +36,8 @@ static void skbmod_explain(void)
                "\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");
 }
 
@@ -61,7 +62,6 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p,
        char *saddr = NULL;
 
        memset(&p, 0, sizeof(p));
-       p.action = TC_ACT_PIPE; /* good default */
 
        if (argc <= 0)
                return -1;
@@ -123,31 +123,7 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p,
                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) {
@@ -206,7 +182,8 @@ static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg)
 
        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]);
index 3ceec1cba616b0e5473ec707cf334a3f55624010..fb418a76bd79aeafe2115f2268cb50a5f64d6a78 100644 (file)
@@ -77,7 +77,7 @@ static int tunnel_key_parse_dst_port(char *str, int type, struct nlmsghdr *n)
 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;
@@ -158,8 +158,8 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
                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) {
@@ -265,7 +265,7 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
                                          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);
index 44b9375966da3df0475776823a688f270418d4ab..cccb4996b05f3f80ebf6faa18917df164e15c8b9 100644 (file)
@@ -32,7 +32,8 @@ static void explain(void)
        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)
@@ -59,7 +60,7 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
        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;
@@ -133,9 +134,8 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
                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) {
@@ -224,7 +224,7 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
                }
                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);
index ff8713b98e315ea6209fb3ab714a7b8fd5e81a8e..b13fb9185d4fdabe1b49080493419e74dbff70cb 100644 (file)
@@ -31,7 +31,7 @@ static void usage(void)
        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"
@@ -59,6 +59,8 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
        __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] = {};
@@ -127,6 +129,13 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
                                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;
@@ -146,6 +155,9 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
 
        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);
 
@@ -167,6 +179,7 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
                        return -1;
                }
        }
+
        if (est.ewma_log)
                addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
 
@@ -193,6 +206,8 @@ static __u32 filter_parent;
 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)
@@ -270,6 +285,15 @@ 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)
@@ -311,6 +335,8 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
        __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] = {};
@@ -375,6 +401,13 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
                                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;
@@ -401,6 +434,9 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
 
        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;
@@ -457,10 +493,20 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
 
 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) {
@@ -470,39 +516,39 @@ static int tc_filter_list(int argc, char **argv)
                                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)
@@ -526,6 +572,14 @@ static int tc_filter_list(int argc, char **argv)
                                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 {
@@ -538,20 +592,23 @@ static int tc_filter_list(int argc, char **argv)
                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;
        }
index 24ca1f1c1c0400972bfd7acd93e88ba568ebda5f..5a0f96aa5562ed7c375ffa9db58316760737ac3a 100644 (file)
@@ -411,10 +411,12 @@ char *sprint_qdisc_handle(__u32 h, char *buf)
        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";
@@ -428,6 +430,8 @@ const char *action_n2a(int action)
                return "pipe";
        case TC_ACT_STOLEN:
                return "stolen";
+       case TC_ACT_TRAP:
+               return "trap";
        default:
                snprintf(buf, 64, "%d", action);
                buf[63] = '\0';
@@ -444,7 +448,7 @@ const char *action_n2a(int action)
  *
  * 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;
@@ -459,6 +463,8 @@ int action_a2n(char *arg, int *result, bool allow_num)
                {"ok", TC_ACT_OK},
                {"reclassify", TC_ACT_RECLASSIFY},
                {"pipe", TC_ACT_PIPE},
+               {"goto", TC_ACT_GOTO_CHAIN},
+               {"trap", TC_ACT_TRAP},
                { NULL },
        }, *iter;
 
@@ -475,6 +481,158 @@ int action_a2n(char *arg, int *result, bool allow_num)
        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;
index 4db26c6d5e25b935457614dee4c5202641aaa505..5c54ad384eae61cb510f102d46dc1936d1978a5a 100644 (file)
@@ -100,8 +100,15 @@ char *sprint_tc_classid(__u32 h, char *buf);
 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);