]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
authorStephen Hemminger <stephen@networkplumber.org>
Fri, 5 Jun 2020 15:33:29 +0000 (08:33 -0700)
committerStephen Hemminger <stephen@networkplumber.org>
Fri, 5 Jun 2020 15:33:29 +0000 (08:33 -0700)
56 files changed:
devlink/devlink.c
include/cg_map.h [new file with mode: 0644]
include/uapi/linux/bpf.h
include/uapi/linux/genetlink.h
include/uapi/linux/if.h
include/uapi/linux/if_bridge.h
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
include/uapi/linux/inet_diag.h
include/uapi/linux/mptcp.h [new file with mode: 0644]
include/uapi/linux/netlink.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/rpl.h [new file with mode: 0644]
include/uapi/linux/rpl_iptunnel.h [new file with mode: 0644]
include/uapi/linux/tc_act/tc_gate.h [new file with mode: 0644]
include/utils.h
ip/Makefile
ip/ip.c
ip/ip_common.h
ip/ipaddress.c
ip/ipmacsec.c
ip/ipmptcp.c [new file with mode: 0644]
ip/iproute.c
ip/iproute_lwtunnel.c
ip/ipvrf.c
ip/link_gre.c
ip/link_gre6.c
lib/Makefile
lib/cg_map.c [new file with mode: 0644]
lib/fs.c
man/man8/devlink-health.8
man/man8/ip-address.8.in
man/man8/ip-link.8.in
man/man8/ip-macsec.8
man/man8/ip-mptcp.8 [new file with mode: 0644]
man/man8/ip.8
man/man8/ss.8
man/man8/tc-flower.8
man/man8/tc-gate.8 [new file with mode: 0644]
man/man8/tc-tunnel_key.8
misc/Makefile
misc/ss.c
misc/ss_util.h [new file with mode: 0644]
misc/ssfilter.h
misc/ssfilter.y
misc/ssfilter_check.c [new file with mode: 0644]
tc/Makefile
tc/f_bpf.c
tc/f_flower.c
tc/m_gate.c [new file with mode: 0644]
tc/m_pedit.c
tc/m_tunnel_key.c
tc/q_mqprio.c
tc/tc_util.c
tipc/link.c

index 16602abf30bd460d6dff7a6dd82cc5ac5e08ce33..507972c360a79754758cba87581cadf5b6e03c80 100644 (file)
@@ -292,6 +292,7 @@ static void ifname_map_free(struct ifname_map *ifname_map)
 #define DL_OPT_TRAP_POLICER_ID         BIT(34)
 #define DL_OPT_TRAP_POLICER_RATE       BIT(35)
 #define DL_OPT_TRAP_POLICER_BURST      BIT(36)
+#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP     BIT(37)
 
 struct dl_opts {
        uint64_t present; /* flags of present items */
@@ -328,6 +329,7 @@ struct dl_opts {
        const char *reporter_name;
        uint64_t reporter_graceful_period;
        bool reporter_auto_recover;
+       bool reporter_auto_dump;
        const char *trap_name;
        const char *trap_group_name;
        enum devlink_trap_action trap_action;
@@ -1474,6 +1476,13 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
                        if (err)
                                return err;
                        o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
+               } else if (dl_argv_match(dl, "auto_dump") &&
+                       (o_all & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)) {
+                       dl_arg_inc(dl);
+                       err = dl_argv_bool(dl, &opts->reporter_auto_dump);
+                       if (err)
+                               return err;
+                       o_found |= DL_OPT_HEALTH_REPORTER_AUTO_DUMP;
                } else if (dl_argv_match(dl, "trap") &&
                           (o_all & DL_OPT_TRAP_NAME)) {
                        dl_arg_inc(dl);
@@ -1656,6 +1665,9 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
        if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
                mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
                                opts->reporter_auto_recover);
+       if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)
+               mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
+                               opts->reporter_auto_dump);
        if (opts->present & DL_OPT_TRAP_NAME)
                mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
                                  opts->trap_name);
@@ -6464,6 +6476,23 @@ static int cmd_region_read(struct dl *dl)
        return err;
 }
 
+static int cmd_region_snapshot_new_cb(const struct nlmsghdr *nlh, void *data)
+{
+       struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+       struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+       struct dl *dl = data;
+
+       mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+       if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+           !tb[DEVLINK_ATTR_REGION_NAME] ||
+           !tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
+               return MNL_CB_ERROR;
+
+       pr_out_region(dl, tb);
+
+       return MNL_CB_OK;
+}
+
 static int cmd_region_snapshot_new(struct dl *dl)
 {
        struct nlmsghdr *nlh;
@@ -6472,12 +6501,15 @@ static int cmd_region_snapshot_new(struct dl *dl)
        nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW,
                               NLM_F_REQUEST | NLM_F_ACK);
 
-       err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
-                               DL_OPT_REGION_SNAPSHOT_ID, 0);
+       err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION,
+                               DL_OPT_REGION_SNAPSHOT_ID);
        if (err)
                return err;
 
-       return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+       pr_out_section_start(dl, "regions");
+       err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_snapshot_new_cb, dl);
+       pr_out_section_end(dl);
+       return err;
 }
 
 static void cmd_region_help(void)
@@ -6525,7 +6557,8 @@ static int cmd_health_set_params(struct dl *dl)
                               NLM_F_REQUEST | NLM_F_ACK);
        err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME,
                            DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD |
-                           DL_OPT_HEALTH_REPORTER_AUTO_RECOVER);
+                           DL_OPT_HEALTH_REPORTER_AUTO_RECOVER |
+                           DL_OPT_HEALTH_REPORTER_AUTO_DUMP);
        if (err)
                return err;
 
@@ -6939,6 +6972,9 @@ static void pr_out_health(struct dl *dl, struct nlattr **tb_health)
        if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
                print_bool(PRINT_ANY, "auto_recover", " auto_recover %s",
                           mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]));
+       if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
+               print_bool(PRINT_ANY, "auto_dump", " auto_dump %s",
+                          mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]));
 
        __pr_out_indent_dec();
        pr_out_handle_end(dl);
@@ -6995,6 +7031,7 @@ static void cmd_health_help(void)
        pr_err("       devlink health set DEV reporter REPORTER_NAME\n");
        pr_err("                          [ grace_period MSEC ]\n");
        pr_err("                          [ auto_recover { true | false } ]\n");
+       pr_err("                          [ auto_dump    { true | false } ]\n");
 }
 
 static int cmd_health(struct dl *dl)
diff --git a/include/cg_map.h b/include/cg_map.h
new file mode 100644 (file)
index 0000000..d30517f
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __CG_MAP_H__
+#define __CG_MAP_H__
+
+const char *cg_id_to_path(__u64 id);
+
+#endif /* __CG_MAP_H__ */
index bc84f10a046fbca169b7546049d43f845083e126..dc5314dd8113f3f943868b6f61f5b9f278f38771 100644 (file)
@@ -113,6 +113,9 @@ enum bpf_cmd {
        BPF_MAP_DELETE_BATCH,
        BPF_LINK_CREATE,
        BPF_LINK_UPDATE,
+       BPF_LINK_GET_FD_BY_ID,
+       BPF_LINK_GET_NEXT_ID,
+       BPF_ENABLE_STATS,
 };
 
 enum bpf_map_type {
@@ -220,6 +223,15 @@ enum bpf_attach_type {
 
 #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
 
+enum bpf_link_type {
+       BPF_LINK_TYPE_UNSPEC = 0,
+       BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
+       BPF_LINK_TYPE_TRACING = 2,
+       BPF_LINK_TYPE_CGROUP = 3,
+
+       MAX_BPF_LINK_TYPE,
+};
+
 /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
  *
  * NONE(default): No further bpf programs allowed in the subtree.
@@ -379,6 +391,12 @@ enum {
  */
 #define BPF_F_QUERY_EFFECTIVE  (1U << 0)
 
+/* type for BPF_ENABLE_STATS */
+enum bpf_stats_type {
+       /* enabled run_time_ns and run_cnt */
+       BPF_STATS_RUN_TIME = 0,
+};
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -523,6 +541,7 @@ union bpf_attr {
                        __u32           prog_id;
                        __u32           map_id;
                        __u32           btf_id;
+                       __u32           link_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -589,6 +608,10 @@ union bpf_attr {
                __u32           old_prog_fd;
        } link_update;
 
+       struct { /* struct used by BPF_ENABLE_STATS command */
+               __u32           type;
+       } enable_stats;
+
 } __attribute__((aligned(8)));
 
 /* The description below is an attempt at providing documentation to eBPF
@@ -652,6 +675,8 @@ union bpf_attr {
  * u64 bpf_ktime_get_ns(void)
  *     Description
  *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does not include time the system was suspended.
+ *             See: clock_gettime(CLOCK_MONOTONIC)
  *     Return
  *             Current *ktime*.
  *
@@ -1562,7 +1587,7 @@ union bpf_attr {
  *     Return
  *             0
  *
- * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **setsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1570,6 +1595,11 @@ union bpf_attr {
  *             must be specified, see **setsockopt(2)** for more information.
  *             The option value of length *optlen* is pointed by *optval*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **setsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -1764,7 +1794,7 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **getsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1773,6 +1803,11 @@ union bpf_attr {
  *             The retrieved value is stored in the structure pointed by
  *             *opval* and of length *optlen*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **getsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -3025,6 +3060,14 @@ union bpf_attr {
  *             * **-EOPNOTSUPP**       Unsupported operation, for example a
  *                                     call from outside of TC ingress.
  *             * **-ESOCKTNOSUPPORT**  Socket type not supported (reuseport).
+ *
+ * u64 bpf_ktime_get_boot_ns(void)
+ *     Description
+ *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does include the time the system was suspended.
+ *             See: clock_gettime(CLOCK_BOOTTIME)
+ *     Return
+ *             Current *ktime*.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -3151,7 +3194,8 @@ union bpf_attr {
        FN(xdp_output),                 \
        FN(get_netns_cookie),           \
        FN(get_current_ancestor_cgroup_id),     \
-       FN(sk_assign),
+       FN(sk_assign),                  \
+       FN(ktime_get_boot_ns),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -3598,6 +3642,25 @@ struct bpf_btf_info {
        __u32 id;
 } __attribute__((aligned(8)));
 
+struct bpf_link_info {
+       __u32 type;
+       __u32 id;
+       __u32 prog_id;
+       union {
+               struct {
+                       __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
+                       __u32 tp_name_len;     /* in/out: tp_name buffer len */
+               } raw_tracepoint;
+               struct {
+                       __u32 attach_type;
+               } tracing;
+               struct {
+                       __u64 cgroup_id;
+                       __u32 attach_type;
+               } cgroup;
+       };
+} __attribute__((aligned(8)));
+
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
  * by user and intended to be used by socket (e.g. to bind to, depends on
  * attach attach type).
index 1317119cbff8f9a4490919be8b2cdf5580bd3e03..7c6c390c48ee7e3c01c9bd283f5dd9000194340f 100644 (file)
@@ -48,6 +48,7 @@ enum {
        CTRL_CMD_NEWMCAST_GRP,
        CTRL_CMD_DELMCAST_GRP,
        CTRL_CMD_GETMCAST_GRP, /* unused */
+       CTRL_CMD_GETPOLICY,
        __CTRL_CMD_MAX,
 };
 
@@ -62,6 +63,7 @@ enum {
        CTRL_ATTR_MAXATTR,
        CTRL_ATTR_OPS,
        CTRL_ATTR_MCAST_GROUPS,
+       CTRL_ATTR_POLICY,
        __CTRL_ATTR_MAX,
 };
 
index 074b14e3844a9c7be0299fb2c20858af6fe9c91f..b287b2a0bb7761a597165ed17666b5d846fcd1b5 100644 (file)
@@ -176,6 +176,7 @@ enum {
 enum {
        IF_LINK_MODE_DEFAULT,
        IF_LINK_MODE_DORMANT,   /* limit upward transition to dormant */
+       IF_LINK_MODE_TESTING,   /* limit upward transition to testing */
 };
 
 /*
index cb581cc03a41215804a5d06edffaaae9cc7f8f8f..a4ac9f5563dd8ba75416586df436cbd5000b7aa0 100644 (file)
@@ -120,6 +120,7 @@ enum {
        IFLA_BRIDGE_MODE,
        IFLA_BRIDGE_VLAN_INFO,
        IFLA_BRIDGE_VLAN_TUNNEL_INFO,
+       IFLA_BRIDGE_MRP,
        __IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -157,6 +158,47 @@ struct bridge_vlan_xstats {
        __u32 pad2;
 };
 
+enum {
+       IFLA_BRIDGE_MRP_UNSPEC,
+       IFLA_BRIDGE_MRP_INSTANCE,
+       IFLA_BRIDGE_MRP_PORT_STATE,
+       IFLA_BRIDGE_MRP_PORT_ROLE,
+       IFLA_BRIDGE_MRP_RING_STATE,
+       IFLA_BRIDGE_MRP_RING_ROLE,
+       IFLA_BRIDGE_MRP_START_TEST,
+       __IFLA_BRIDGE_MRP_MAX,
+};
+
+struct br_mrp_instance {
+       __u32 ring_id;
+       __u32 p_ifindex;
+       __u32 s_ifindex;
+};
+
+struct br_mrp_port_role {
+       __u32 ring_id;
+       __u32 role;
+};
+
+struct br_mrp_ring_state {
+       __u32 ring_id;
+       __u32 ring_state;
+};
+
+struct br_mrp_ring_role {
+       __u32 ring_id;
+       __u32 ring_role;
+};
+
+struct br_mrp_start_test {
+       __u32 ring_id;
+       __u32 interval;
+       __u32 max_miss;
+       __u32 period;
+};
+
+#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1)
+
 struct bridge_stp_xstats {
        __u64 transition_blk;
        __u64 transition_fwd;
index 728c42dfd59c13fa8b12e5991a85440d6d084ad5..1a0c7dfe8e38e071b80cfd229f2f7fae69810d12 100644 (file)
@@ -92,6 +92,7 @@
 #define ETH_P_PREAUTH  0x88C7          /* 802.11 Preauthentication */
 #define ETH_P_TIPC     0x88CA          /* TIPC                         */
 #define ETH_P_LLDP     0x88CC          /* Link Layer Discovery Protocol */
+#define ETH_P_MRP      0x88E3          /* Media Redundancy Protocol    */
 #define ETH_P_MACSEC   0x88E5          /* 802.1ae MACsec */
 #define ETH_P_8021AH   0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_MVRP     0x88F5          /* 802.1Q MVRP                  */
index 978f98c76be17c262ff5347a5af60f0b2fbed8b6..a8901a39a345ecfc951acc35bda3dfdd63277f51 100644 (file)
@@ -341,6 +341,7 @@ enum {
        IFLA_BRPORT_NEIGH_SUPPRESS,
        IFLA_BRPORT_ISOLATED,
        IFLA_BRPORT_BACKUP_PORT,
+       IFLA_BRPORT_MRP_RING_OPEN,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
index 0c1c781c0a60f6d201d252c082487812cd426e42..f009abf1e0bfd9ea008661d1ecf9f4d1c5ac41b8 100644 (file)
@@ -96,6 +96,7 @@ enum {
        INET_DIAG_BC_MARK_COND,
        INET_DIAG_BC_S_EQ,
        INET_DIAG_BC_D_EQ,
+       INET_DIAG_BC_CGROUP_COND,   /* u64 cgroup v2 ID */
 };
 
 struct inet_diag_hostcond {
@@ -157,6 +158,7 @@ enum {
        INET_DIAG_MD5SIG,
        INET_DIAG_ULP_INFO,
        INET_DIAG_SK_BPF_STORAGES,
+       INET_DIAG_CGROUP_ID,
        __INET_DIAG_MAX,
 };
 
diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
new file mode 100644 (file)
index 0000000..009b8f0
--- /dev/null
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+#ifndef _MPTCP_H
+#define _MPTCP_H
+
+#include <linux/const.h>
+#include <linux/types.h>
+
+#define MPTCP_SUBFLOW_FLAG_MCAP_REM            _BITUL(0)
+#define MPTCP_SUBFLOW_FLAG_MCAP_LOC            _BITUL(1)
+#define MPTCP_SUBFLOW_FLAG_JOIN_REM            _BITUL(2)
+#define MPTCP_SUBFLOW_FLAG_JOIN_LOC            _BITUL(3)
+#define MPTCP_SUBFLOW_FLAG_BKUP_REM            _BITUL(4)
+#define MPTCP_SUBFLOW_FLAG_BKUP_LOC            _BITUL(5)
+#define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED   _BITUL(6)
+#define MPTCP_SUBFLOW_FLAG_CONNECTED           _BITUL(7)
+#define MPTCP_SUBFLOW_FLAG_MAPVALID            _BITUL(8)
+
+enum {
+       MPTCP_SUBFLOW_ATTR_UNSPEC,
+       MPTCP_SUBFLOW_ATTR_TOKEN_REM,
+       MPTCP_SUBFLOW_ATTR_TOKEN_LOC,
+       MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ,
+       MPTCP_SUBFLOW_ATTR_MAP_SEQ,
+       MPTCP_SUBFLOW_ATTR_MAP_SFSEQ,
+       MPTCP_SUBFLOW_ATTR_SSN_OFFSET,
+       MPTCP_SUBFLOW_ATTR_MAP_DATALEN,
+       MPTCP_SUBFLOW_ATTR_FLAGS,
+       MPTCP_SUBFLOW_ATTR_ID_REM,
+       MPTCP_SUBFLOW_ATTR_ID_LOC,
+       MPTCP_SUBFLOW_ATTR_PAD,
+       __MPTCP_SUBFLOW_ATTR_MAX
+};
+
+#define MPTCP_SUBFLOW_ATTR_MAX (__MPTCP_SUBFLOW_ATTR_MAX - 1)
+
+/* netlink interface */
+#define MPTCP_PM_NAME          "mptcp_pm"
+#define MPTCP_PM_CMD_GRP_NAME  "mptcp_pm_cmds"
+#define MPTCP_PM_VER           0x1
+
+/*
+ * ATTR types defined for MPTCP
+ */
+enum {
+       MPTCP_PM_ATTR_UNSPEC,
+
+       MPTCP_PM_ATTR_ADDR,                             /* nested address */
+       MPTCP_PM_ATTR_RCV_ADD_ADDRS,                    /* u32 */
+       MPTCP_PM_ATTR_SUBFLOWS,                         /* u32 */
+
+       __MPTCP_PM_ATTR_MAX
+};
+
+#define MPTCP_PM_ATTR_MAX (__MPTCP_PM_ATTR_MAX - 1)
+
+enum {
+       MPTCP_PM_ADDR_ATTR_UNSPEC,
+
+       MPTCP_PM_ADDR_ATTR_FAMILY,                      /* u16 */
+       MPTCP_PM_ADDR_ATTR_ID,                          /* u8 */
+       MPTCP_PM_ADDR_ATTR_ADDR4,                       /* struct in_addr */
+       MPTCP_PM_ADDR_ATTR_ADDR6,                       /* struct in6_addr */
+       MPTCP_PM_ADDR_ATTR_PORT,                        /* u16 */
+       MPTCP_PM_ADDR_ATTR_FLAGS,                       /* u32 */
+       MPTCP_PM_ADDR_ATTR_IF_IDX,                      /* s32 */
+
+       __MPTCP_PM_ADDR_ATTR_MAX
+};
+
+#define MPTCP_PM_ADDR_ATTR_MAX (__MPTCP_PM_ADDR_ATTR_MAX - 1)
+
+#define MPTCP_PM_ADDR_FLAG_SIGNAL                      (1 << 0)
+#define MPTCP_PM_ADDR_FLAG_SUBFLOW                     (1 << 1)
+#define MPTCP_PM_ADDR_FLAG_BACKUP                      (1 << 2)
+
+enum {
+       MPTCP_PM_CMD_UNSPEC,
+
+       MPTCP_PM_CMD_ADD_ADDR,
+       MPTCP_PM_CMD_DEL_ADDR,
+       MPTCP_PM_CMD_GET_ADDR,
+       MPTCP_PM_CMD_FLUSH_ADDRS,
+       MPTCP_PM_CMD_SET_LIMITS,
+       MPTCP_PM_CMD_GET_LIMITS,
+
+       __MPTCP_PM_CMD_AFTER_LAST
+};
+
+#endif /* _MPTCP_H */
index 2c28d329e5955f71000cfc2951f06c0d882352a8..695c88e3c29d9bfbdfa69d96dfdd44b0ee86f8f0 100644 (file)
@@ -245,4 +245,107 @@ struct nla_bitfield32 {
        __u32 selector;
 };
 
+/*
+ * policy descriptions - it's specific to each family how this is used
+ * Normally, it should be retrieved via a dump inside another attribute
+ * specifying where it applies.
+ */
+
+/**
+ * enum netlink_attribute_type - type of an attribute
+ * @NL_ATTR_TYPE_INVALID: unused
+ * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
+ * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
+ * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
+ * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
+ * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
+ * @NL_ATTR_TYPE_S8: 8-bit signed attribute
+ * @NL_ATTR_TYPE_S16: 16-bit signed attribute
+ * @NL_ATTR_TYPE_S32: 32-bit signed attribute
+ * @NL_ATTR_TYPE_S64: 64-bit signed attribute
+ * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
+ * @NL_ATTR_TYPE_STRING: string, min/max length may be specified
+ * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
+ *     min/max length may be specified
+ * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
+ *     consists of sub-attributes. The nested policy and maxtype
+ *     inside may be specified.
+ * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
+ *     attribute contains sub-attributes whose type is irrelevant
+ *     (just used to separate the array entries) and each such array
+ *     entry has attributes again, the policy for those inner ones
+ *     and the corresponding maxtype may be specified.
+ * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ */
+enum netlink_attribute_type {
+       NL_ATTR_TYPE_INVALID,
+
+       NL_ATTR_TYPE_FLAG,
+
+       NL_ATTR_TYPE_U8,
+       NL_ATTR_TYPE_U16,
+       NL_ATTR_TYPE_U32,
+       NL_ATTR_TYPE_U64,
+
+       NL_ATTR_TYPE_S8,
+       NL_ATTR_TYPE_S16,
+       NL_ATTR_TYPE_S32,
+       NL_ATTR_TYPE_S64,
+
+       NL_ATTR_TYPE_BINARY,
+       NL_ATTR_TYPE_STRING,
+       NL_ATTR_TYPE_NUL_STRING,
+
+       NL_ATTR_TYPE_NESTED,
+       NL_ATTR_TYPE_NESTED_ARRAY,
+
+       NL_ATTR_TYPE_BITFIELD32,
+};
+
+/**
+ * enum netlink_policy_type_attr - policy type attributes
+ * @NL_POLICY_TYPE_ATTR_UNSPEC: unused
+ * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
+ *     &enum netlink_attribute_type (U32)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
+ *     integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
+ *     integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
+ *     integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
+ *     integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
+ *     attributes, no minimum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
+ *     attributes, no maximum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
+ *     nested array types (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
+ *     attribute for nested and nested array types, this can
+ *     in theory be < the size of the policy pointed to by
+ *     the index, if limited inside the nesting (U32)
+ * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
+ *     bitfield32 type (U32)
+ * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
+ */
+enum netlink_policy_type_attr {
+       NL_POLICY_TYPE_ATTR_UNSPEC,
+       NL_POLICY_TYPE_ATTR_TYPE,
+       NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+       NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+       NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+       NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+       NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+       NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+       NL_POLICY_TYPE_ATTR_POLICY_IDX,
+       NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+       NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+       NL_POLICY_TYPE_ATTR_PAD,
+
+       /* keep last */
+       __NL_POLICY_TYPE_ATTR_MAX,
+       NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
+};
+
 #endif /* __LINUX_NETLINK_H */
index 9f06d29cab705871c3b2b78a15284b429314849c..fc672b232437dcc4578aa90be14ae51caa69e099 100644 (file)
@@ -134,6 +134,7 @@ enum tca_id {
        TCA_ID_CTINFO,
        TCA_ID_MPLS,
        TCA_ID_CT,
+       TCA_ID_GATE,
        /* other actions go here */
        __TCA_ID_MAX = 255
 };
index 0c02737c8f47921b807e52d6482ca9ff84e89268..a95f3ae7ab37c857ff09cba39eca65fea9ce92aa 100644 (file)
@@ -913,6 +913,10 @@ enum {
 
        TCA_FQ_TIMER_SLACK,     /* timer slack */
 
+       TCA_FQ_HORIZON,         /* time horizon in us */
+
+       TCA_FQ_HORIZON_DROP,    /* drop packets beyond horizon, or cap their EDT */
+
        __TCA_FQ_MAX
 };
 
@@ -932,6 +936,8 @@ struct tc_fq_qd_stats {
        __u32   throttled_flows;
        __u32   unthrottle_latency_ns;
        __u64   ce_mark;                /* packets above ce_threshold */
+       __u64   horizon_drops;
+       __u64   horizon_caps;
 };
 
 /* Heavy-Hitter Filter */
diff --git a/include/uapi/linux/rpl.h b/include/uapi/linux/rpl.h
new file mode 100644 (file)
index 0000000..c24b64c
--- /dev/null
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ *  IPv6 RPL-SR implementation
+ *
+ *  Author:
+ *  (C) 2020 Alexander Aring <alex.aring@gmail.com>
+ */
+
+#ifndef _LINUX_RPL_H
+#define _LINUX_RPL_H
+
+#include <asm/byteorder.h>
+#include <linux/types.h>
+#include <linux/in6.h>
+
+/*
+ * RPL SR Header
+ */
+struct ipv6_rpl_sr_hdr {
+       __u8    nexthdr;
+       __u8    hdrlen;
+       __u8    type;
+       __u8    segments_left;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+       __u32   cmpre:4,
+               cmpri:4,
+               reserved:4,
+               pad:4,
+               reserved1:16;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+       __u32   reserved:20,
+               pad:4,
+               cmpri:4,
+               cmpre:4;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
+
+       union {
+               struct in6_addr addr[0];
+               __u8 data[0];
+       } segments;
+} __attribute__((packed));
+
+#define rpl_segaddr    segments.addr
+#define rpl_segdata    segments.data
+
+#endif
diff --git a/include/uapi/linux/rpl_iptunnel.h b/include/uapi/linux/rpl_iptunnel.h
new file mode 100644 (file)
index 0000000..c255b92
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ *  IPv6 RPL-SR implementation
+ *
+ *  Author:
+ *  (C) 2020 Alexander Aring <alex.aring@gmail.com>
+ */
+
+#ifndef _LINUX_RPL_IPTUNNEL_H
+#define _LINUX_RPL_IPTUNNEL_H
+
+enum {
+       RPL_IPTUNNEL_UNSPEC,
+       RPL_IPTUNNEL_SRH,
+       __RPL_IPTUNNEL_MAX,
+};
+#define RPL_IPTUNNEL_MAX (__RPL_IPTUNNEL_MAX - 1)
+
+#define RPL_IPTUNNEL_SRH_SIZE(srh) (((srh)->hdrlen + 1) << 3)
+
+#endif
diff --git a/include/uapi/linux/tc_act/tc_gate.h b/include/uapi/linux/tc_act/tc_gate.h
new file mode 100644 (file)
index 0000000..f214b3a
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* Copyright 2020 NXP */
+
+#ifndef __LINUX_TC_GATE_H
+#define __LINUX_TC_GATE_H
+
+#include <linux/pkt_cls.h>
+
+struct tc_gate {
+       tc_gen;
+};
+
+enum {
+       TCA_GATE_ENTRY_UNSPEC,
+       TCA_GATE_ENTRY_INDEX,
+       TCA_GATE_ENTRY_GATE,
+       TCA_GATE_ENTRY_INTERVAL,
+       TCA_GATE_ENTRY_IPV,
+       TCA_GATE_ENTRY_MAX_OCTETS,
+       __TCA_GATE_ENTRY_MAX,
+};
+#define TCA_GATE_ENTRY_MAX (__TCA_GATE_ENTRY_MAX - 1)
+
+enum {
+       TCA_GATE_ONE_ENTRY_UNSPEC,
+       TCA_GATE_ONE_ENTRY,
+       __TCA_GATE_ONE_ENTRY_MAX,
+};
+#define TCA_GATE_ONE_ENTRY_MAX (__TCA_GATE_ONE_ENTRY_MAX - 1)
+
+enum {
+       TCA_GATE_UNSPEC,
+       TCA_GATE_TM,
+       TCA_GATE_PARMS,
+       TCA_GATE_PAD,
+       TCA_GATE_PRIORITY,
+       TCA_GATE_ENTRY_LIST,
+       TCA_GATE_BASE_TIME,
+       TCA_GATE_CYCLE_TIME,
+       TCA_GATE_CYCLE_TIME_EXT,
+       TCA_GATE_FLAGS,
+       TCA_GATE_CLOCKID,
+       __TCA_GATE_MAX,
+};
+#define TCA_GATE_MAX (__TCA_GATE_MAX - 1)
+
+#endif
index 001491a1eb405f9143c7f4ca6ea81f0615651e84..7041c4612e4632b40a5ce8d5b676ed65639b891f 100644 (file)
@@ -302,7 +302,9 @@ int get_real_family(int rtm_type, int rtm_family);
 int cmd_exec(const char *cmd, char **argv, bool do_fork,
             int (*setup)(void *), void *arg);
 int make_path(const char *path, mode_t mode);
-char *find_cgroup2_mount(void);
+char *find_cgroup2_mount(bool do_mount);
+__u64 get_cgroup2_id(const char *path);
+char *get_cgroup2_path(__u64 id, bool full);
 int get_command_name(const char *pid, char *comm, size_t len);
 
 int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
index 5ab78d7d3b84e1d036de5859b802d1f0eec29fe5..8735b8e4706b3bfb1e0420eab8662cf2d19b2c9a 100644 (file)
@@ -11,7 +11,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
     iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
     ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
-    ipnexthop.o
+    ipnexthop.o ipmptcp.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index 90392c2acfa05540404e8d1db5d6df20c4435827..4249df0377f9e20a0cb329d750ac81d4ef337e6a 100644 (file)
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -51,7 +51,7 @@ static void usage(void)
                "where  OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n"
                "                   tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
                "                   netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n"
-               "                   vrf | sr | nexthop }\n"
+               "                   vrf | sr | nexthop | mptcp }\n"
                "       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
                "                    -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
                "                    -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
@@ -103,6 +103,7 @@ static const struct cmd {
        { "vrf",        do_ipvrf},
        { "sr",         do_seg6 },
        { "nexthop",    do_ipnh },
+       { "mptcp",      do_mptcp },
        { "help",       do_help },
        { 0 }
 };
index 879287e3e5060a981778f5b7b22ae82a58fdcade..d604f7554405ade78abd7aec84325136a57871e8 100644 (file)
@@ -83,6 +83,7 @@ void vrf_reset(void);
 int netns_identify_pid(const char *pidstr, char *name, int len);
 int do_seg6(int argc, char **argv);
 int do_ipnh(int argc, char **argv);
+int do_mptcp(int argc, char **argv);
 
 int iplink_get(char *name, __u32 filt_mask);
 int iplink_ifla_xstats(int argc, char **argv);
index 80d27ce27d0c14c34ed796093dc45d47b5cb8d6b..3b53933f41673a5366aa567f75e8d94640e1ec7b 100644 (file)
@@ -1233,52 +1233,63 @@ static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
                ifa->ifa_flags;
 }
 
-/* Mapping from argument to address flag mask */
-static const struct {
+/* Mapping from argument to address flag mask and attributes */
+static const struct ifa_flag_data_t {
        const char *name;
-       unsigned long value;
-} ifa_flag_names[] = {
-       { "secondary",          IFA_F_SECONDARY },
-       { "temporary",          IFA_F_SECONDARY },
-       { "nodad",              IFA_F_NODAD },
-       { "optimistic",         IFA_F_OPTIMISTIC },
-       { "dadfailed",          IFA_F_DADFAILED },
-       { "home",               IFA_F_HOMEADDRESS },
-       { "deprecated",         IFA_F_DEPRECATED },
-       { "tentative",          IFA_F_TENTATIVE },
-       { "permanent",          IFA_F_PERMANENT },
-       { "mngtmpaddr",         IFA_F_MANAGETEMPADDR },
-       { "noprefixroute",      IFA_F_NOPREFIXROUTE },
-       { "autojoin",           IFA_F_MCAUTOJOIN },
-       { "stable-privacy",     IFA_F_STABLE_PRIVACY },
+       unsigned long mask;
+       bool readonly;
+       bool v6only;
+} ifa_flag_data[] = {
+       { .name = "secondary",          .mask = IFA_F_SECONDARY,        .readonly = true,       .v6only = false},
+       { .name = "temporary",          .mask = IFA_F_SECONDARY,        .readonly = true,       .v6only = false},
+       { .name = "nodad",              .mask = IFA_F_NODAD,            .readonly = false,      .v6only = true},
+       { .name = "optimistic",         .mask = IFA_F_OPTIMISTIC,       .readonly = false,      .v6only = true},
+       { .name = "dadfailed",          .mask = IFA_F_DADFAILED,        .readonly = true,       .v6only = true},
+       { .name = "home",               .mask = IFA_F_HOMEADDRESS,      .readonly = false,      .v6only = true},
+       { .name = "deprecated",         .mask = IFA_F_DEPRECATED,       .readonly = true,       .v6only = true},
+       { .name = "tentative",          .mask = IFA_F_TENTATIVE,        .readonly = true,       .v6only = true},
+       { .name = "permanent",          .mask = IFA_F_PERMANENT,        .readonly = true,       .v6only = true},
+       { .name = "mngtmpaddr",         .mask = IFA_F_MANAGETEMPADDR,   .readonly = false,      .v6only = true},
+       { .name = "noprefixroute",      .mask = IFA_F_NOPREFIXROUTE,    .readonly = false,      .v6only = true},
+       { .name = "autojoin",           .mask = IFA_F_MCAUTOJOIN,       .readonly = false,      .v6only = true},
+       { .name = "stable-privacy",     .mask = IFA_F_STABLE_PRIVACY,   .readonly = true,       .v6only = true},
 };
 
+/* Returns a pointer to the data structure for a particular interface flag, or null if no flag could be found */
+static const struct ifa_flag_data_t* lookup_flag_data_by_name(const char* flag_name) {
+       for (int i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) {
+               if (strcmp(flag_name, ifa_flag_data[i].name) == 0)
+                       return &ifa_flag_data[i];
+       }
+        return NULL;
+}
+
 static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
                            unsigned int flags)
 {
        unsigned int i;
 
-       for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
-               unsigned long mask = ifa_flag_names[i].value;
+       for (i = 0; i < ARRAY_SIZE(ifa_flag_data); i++) {
+               const struct ifa_flag_data_t* flag_data = &ifa_flag_data[i];
 
-               if (mask == IFA_F_PERMANENT) {
-                       if (!(flags & mask))
+               if (flag_data->mask == IFA_F_PERMANENT) {
+                       if (!(flags & flag_data->mask))
                                print_bool(PRINT_ANY,
                                           "dynamic", "dynamic ", true);
-               } else if (flags & mask) {
-                       if (mask == IFA_F_SECONDARY &&
+               } else if (flags & flag_data->mask) {
+                       if (flag_data->mask == IFA_F_SECONDARY &&
                            ifa->ifa_family == AF_INET6) {
                                print_bool(PRINT_ANY,
                                           "temporary", "temporary ", true);
                        } else {
                                print_string(PRINT_FP, NULL,
-                                            "%s ", ifa_flag_names[i].name);
+                                            "%s ", flag_data->name);
                                print_bool(PRINT_JSON,
-                                          ifa_flag_names[i].name, NULL, true);
+                                          flag_data->name, NULL, true);
                        }
                }
 
-               flags &= ~mask;
+               flags &= ~flag_data->mask;
        }
 
        if (flags) {
@@ -1297,7 +1308,6 @@ static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
 static int get_filter(const char *arg)
 {
        bool inv = false;
-       unsigned int i;
 
        if (arg[0] == '-') {
                inv = true;
@@ -1313,18 +1323,16 @@ static int get_filter(const char *arg)
                arg = "secondary";
        }
 
-       for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
-               if (strcmp(arg, ifa_flag_names[i].name))
-                       continue;
+       const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(arg);
+       if (flag_data == NULL)
+               return -1;
 
-               if (inv)
-                       filter.flags &= ~ifa_flag_names[i].value;
-               else
-                       filter.flags |= ifa_flag_names[i].value;
-               filter.flagmask |= ifa_flag_names[i].value;
-               return 0;
-       }
-       return -1;
+       if (inv)
+               filter.flags &= ~flag_data->mask;
+       else
+               filter.flags |= flag_data->mask;
+       filter.flagmask |= flag_data->mask;
+       return 0;
 }
 
 static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
@@ -2330,25 +2338,15 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
                        preferred_lftp = *argv;
                        if (set_lifetime(&preferred_lft, *argv))
                                invarg("preferred_lft value", *argv);
-               } else if (strcmp(*argv, "home") == 0) {
-                       if (req.ifa.ifa_family == AF_INET6)
-                               ifa_flags |= IFA_F_HOMEADDRESS;
-                       else
-                               fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n");
-               } else if (strcmp(*argv, "nodad") == 0) {
-                       if (req.ifa.ifa_family == AF_INET6)
-                               ifa_flags |= IFA_F_NODAD;
-                       else
-                               fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n");
-               } else if (strcmp(*argv, "mngtmpaddr") == 0) {
-                       if (req.ifa.ifa_family == AF_INET6)
-                               ifa_flags |= IFA_F_MANAGETEMPADDR;
-                       else
-                               fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n");
-               } else if (strcmp(*argv, "noprefixroute") == 0) {
-                       ifa_flags |= IFA_F_NOPREFIXROUTE;
-               } else if (strcmp(*argv, "autojoin") == 0) {
-                       ifa_flags |= IFA_F_MCAUTOJOIN;
+               } else if (lookup_flag_data_by_name(*argv)) {
+                       const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(*argv);
+                       if (flag_data->readonly) {
+                               fprintf(stderr, "Warning: %s option is not mutable from userspace\n", flag_data->name);
+                       } else if (flag_data->v6only && req.ifa.ifa_family != AF_INET6) {
+                               fprintf(stderr, "Warning: %s option can be set only for IPv6 addresses\n", flag_data->name);
+                       } else {
+                               ifa_flags |= flag_data->mask;
+                       }
                } else {
                        if (strcmp(*argv, "local") == 0)
                                NEXT_ARG();
index 4e500e4e4825d0ee40dc789f82fe36026d945d01..18289ecd6d9e0841b493e721c6acc192903cdb47 100644 (file)
@@ -34,6 +34,7 @@ static const char * const validate_str[] = {
 static const char * const offload_str[] = {
        [MACSEC_OFFLOAD_OFF] = "off",
        [MACSEC_OFFLOAD_PHY] = "phy",
+       [MACSEC_OFFLOAD_MAC] = "mac",
 };
 
 struct sci {
@@ -98,7 +99,7 @@ static void ipmacsec_usage(void)
                "       ip macsec del DEV rx SCI sa { 0..3 }\n"
                "       ip macsec show\n"
                "       ip macsec show DEV\n"
-               "       ip macsec offload DEV [ off | phy ]\n"
+               "       ip macsec offload DEV [ off | phy | mac ]\n"
                "where  OPTS := [ pn <u32> ] [ on | off ]\n"
                "       ID   := 128-bit hex string\n"
                "       KEY  := 128-bit or 256-bit hex string\n"
@@ -1219,6 +1220,15 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
                             validate_to_str(val));
        }
 
+       if (tb[IFLA_MACSEC_OFFLOAD]) {
+               __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_OFFLOAD]);
+
+               print_string(PRINT_ANY,
+                            "offload",
+                            "offload %s ",
+                            offload_to_str(val));
+       }
+
        const char *inc_sci, *es, *replay;
 
        if (is_json_context()) {
@@ -1267,6 +1277,7 @@ static void usage(FILE *f)
                "                  [ replay { on | off} window { 0..2^32-1 } ]\n"
                "                  [ validate { strict | check | disabled } ]\n"
                "                  [ encodingsa { 0..3 } ]\n"
+               "                  [ offload { mac | phy | off } ]\n"
                );
 }
 
@@ -1276,6 +1287,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
        int ret;
        __u8 encoding_sa = 0xff;
        __u32 window = -1;
+       enum macsec_offload offload;
        struct cipher_args cipher = {0};
        enum macsec_validation_type validate;
        bool es = false, scb = false, send_sci = false;
@@ -1397,6 +1409,15 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
                        ret = get_an(&encoding_sa, *argv);
                        if (ret)
                                invarg("expected an { 0..3 }", *argv);
+               } else if (strcmp(*argv, "offload") == 0) {
+                       NEXT_ARG();
+                       ret = one_of("offload", *argv,
+                                    offload_str, ARRAY_SIZE(offload_str),
+                                    (int *)&offload);
+                       if (ret != 0)
+                               return ret;
+                       addattr8(n, MACSEC_BUFLEN,
+                                IFLA_MACSEC_OFFLOAD, offload);
                } else {
                        fprintf(stderr, "macsec: unknown command \"%s\"?\n",
                                *argv);
diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c
new file mode 100644 (file)
index 0000000..bc12418
--- /dev/null
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdio.h>
+#include <string.h>
+#include <rt_names.h>
+#include <errno.h>
+
+#include <linux/genetlink.h>
+#include <linux/mptcp.h>
+
+#include "utils.h"
+#include "ip_common.h"
+#include "libgenl.h"
+#include "json_print.h"
+
+static void usage(void)
+{
+       fprintf(stderr,
+               "Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
+               "                                     [ FLAG-LIST ]\n"
+               "       ip mptcp endpoint delete id ID\n"
+               "       ip mptcp endpoint show [ id ID ]\n"
+               "       ip mptcp endpoint flush\n"
+               "       ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
+               "       ip mptcp limits show\n"
+               "FLAG-LIST := [ FLAG-LIST ] FLAG\n"
+               "FLAG  := [ signal | subflow | backup ]\n");
+
+       exit(-1);
+}
+
+/* netlink socket */
+static struct rtnl_handle genl_rth = { .fd = -1 };
+static int genl_family = -1;
+
+#define MPTCP_BUFLEN   4096
+#define MPTCP_REQUEST(_req,  _cmd, _flags)     \
+       GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0,        \
+                    MPTCP_PM_VER, _cmd, _flags)
+
+/* Mapping from argument to address flag mask */
+static const struct {
+       const char *name;
+       unsigned long value;
+} mptcp_addr_flag_names[] = {
+       { "signal",             MPTCP_PM_ADDR_FLAG_SIGNAL },
+       { "subflow",            MPTCP_PM_ADDR_FLAG_SUBFLOW },
+       { "backup",             MPTCP_PM_ADDR_FLAG_BACKUP },
+};
+
+static void print_mptcp_addr_flags(unsigned int flags)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
+               unsigned long mask = mptcp_addr_flag_names[i].value;
+
+               if (flags & mask) {
+                       print_string(PRINT_FP, NULL, "%s ",
+                                    mptcp_addr_flag_names[i].name);
+                       print_bool(PRINT_JSON,
+                                  mptcp_addr_flag_names[i].name, NULL, true);
+               }
+
+               flags &= ~mask;
+       }
+
+       if (flags) {
+               /* unknown flags */
+               SPRINT_BUF(b1);
+
+               snprintf(b1, sizeof(b1), "%02x", flags);
+               print_string(PRINT_ANY, "rawflags", "rawflags %s ", b1);
+       }
+}
+
+static int get_flags(const char *arg, __u32 *flags)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
+               if (strcmp(arg, mptcp_addr_flag_names[i].name))
+                       continue;
+
+               *flags |= mptcp_addr_flag_names[i].value;
+               return 0;
+       }
+       return -1;
+}
+
+static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
+                        bool adding)
+{
+       struct rtattr *attr_addr;
+       bool addr_set = false;
+       inet_prefix address;
+       bool id_set = false;
+       __u32 index = 0;
+       __u32 flags = 0;
+       __u8 id = 0;
+
+       ll_init_map(&rth);
+       while (argc > 0) {
+               if (get_flags(*argv, &flags) == 0) {
+               } else if (matches(*argv, "id") == 0) {
+                       NEXT_ARG();
+
+                       if (get_u8(&id, *argv, 0))
+                               invarg("invalid ID\n", *argv);
+                       id_set = true;
+               } else if (matches(*argv, "dev") == 0) {
+                       const char *ifname;
+
+                       NEXT_ARG();
+
+                       ifname = *argv;
+
+                       if (check_ifname(ifname))
+                               invarg("invalid interface name\n", ifname);
+
+                       index = ll_name_to_index(ifname);
+
+                       if (!index)
+                               invarg("device does not exist\n", ifname);
+
+               } else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
+                       addr_set = true;
+               } else {
+                       invarg("unknown argument", *argv);
+               }
+               NEXT_ARG_FWD();
+       }
+
+       if (!addr_set && adding)
+               missarg("ADDRESS");
+
+       if (!id_set && !adding)
+               missarg("ID");
+
+       attr_addr = addattr_nest(n, MPTCP_BUFLEN,
+                                MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
+       if (id_set)
+               addattr8(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_ID, id);
+       if (flags)
+               addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
+       if (index)
+               addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
+       if (addr_set) {
+               int type;
+
+               addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FAMILY,
+                         address.family);
+               type = address.family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
+                                                  MPTCP_PM_ADDR_ATTR_ADDR6;
+               addattr_l(n, MPTCP_BUFLEN, type, &address.data,
+                         address.bytelen);
+       }
+
+       addattr_nest_end(n, attr_addr);
+       return 0;
+}
+
+static int mptcp_addr_modify(int argc, char **argv, int cmd)
+{
+       MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
+       int ret;
+
+       ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR);
+       if (ret)
+               return ret;
+
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+               return -2;
+
+       return 0;
+}
+
+static int print_mptcp_addrinfo(struct rtattr *addrinfo)
+{
+       struct rtattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
+       __u8 family = AF_UNSPEC, addr_attr_type;
+       const char *ifname;
+       unsigned int flags;
+       int index;
+       __u16 id;
+
+       parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
+
+       open_json_object(NULL);
+       if (tb[MPTCP_PM_ADDR_ATTR_FAMILY])
+               family = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
+
+       addr_attr_type = family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
+                                            MPTCP_PM_ADDR_ATTR_ADDR6;
+       if (tb[addr_attr_type]) {
+               print_string(PRINT_ANY, "address", "%s ",
+                            format_host_rta(family, tb[addr_attr_type]));
+       }
+       if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
+               id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
+               print_uint(PRINT_ANY, "id", "id %u ", id);
+       }
+       if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) {
+               flags = rta_getattr_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
+               print_mptcp_addr_flags(flags);
+       }
+       if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) {
+               index = rta_getattr_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
+               ifname = index ? ll_index_to_name(index) : NULL;
+
+               if (ifname)
+                       print_string(PRINT_ANY, "dev", "dev %s ", ifname);
+       }
+
+       close_json_object();
+       print_string(PRINT_FP, NULL, "\n", NULL);
+       fflush(stdout);
+
+       return 0;
+}
+
+static int print_mptcp_addr(struct nlmsghdr *n, void *arg)
+{
+       struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
+       struct genlmsghdr *ghdr;
+       struct rtattr *addrinfo;
+       int len = n->nlmsg_len;
+
+       if (n->nlmsg_type != genl_family)
+               return 0;
+
+       len -= NLMSG_LENGTH(GENL_HDRLEN);
+       if (len < 0)
+               return -1;
+
+       ghdr = NLMSG_DATA(n);
+       parse_rtattr_flags(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN,
+                          len, NLA_F_NESTED);
+       addrinfo = tb[MPTCP_PM_ATTR_ADDR];
+       if (!addrinfo)
+               return -1;
+
+       ll_init_map(&rth);
+       return print_mptcp_addrinfo(addrinfo);
+}
+
+static int mptcp_addr_dump(void)
+{
+       MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST | NLM_F_DUMP);
+
+       if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
+               perror("Cannot send show request");
+               exit(1);
+       }
+
+       new_json_obj(json);
+
+       if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               delete_json_obj();
+               fflush(stdout);
+               return -2;
+       }
+
+       close_json_object();
+       fflush(stdout);
+       return 0;
+}
+
+static int mptcp_addr_show(int argc, char **argv)
+{
+       MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST);
+       struct nlmsghdr *answer;
+       int ret;
+
+       if (!argv)
+               return mptcp_addr_dump();
+
+       ret = mptcp_parse_opt(argc, argv, &req.n, false);
+       if (ret)
+               return ret;
+
+       if (rtnl_talk(&genl_rth, &req.n, &answer) < 0)
+               return -2;
+
+       return print_mptcp_addr(answer, stdout);
+}
+
+static int mptcp_addr_flush(int argc, char **argv)
+{
+       MPTCP_REQUEST(req, MPTCP_PM_CMD_FLUSH_ADDRS, NLM_F_REQUEST);
+
+       if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
+               return -2;
+
+       return 0;
+}
+
+static int mptcp_parse_limit(int argc, char **argv, struct nlmsghdr *n)
+{
+       bool set_rcv_add_addrs = false;
+       bool set_subflows = false;
+       __u32 rcv_add_addrs = 0;
+       __u32 subflows = 0;
+
+       while (argc > 0) {
+               if (matches(*argv, "subflows") == 0) {
+                       NEXT_ARG();
+
+                       if (get_u32(&subflows, *argv, 0))
+                               invarg("invalid subflows\n", *argv);
+                       set_subflows = true;
+               } else if (matches(*argv, "add_addr_accepted") == 0) {
+                       NEXT_ARG();
+
+                       if (get_u32(&rcv_add_addrs, *argv, 0))
+                               invarg("invalid add_addr_accepted\n", *argv);
+                       set_rcv_add_addrs = true;
+               } else {
+                       invarg("unknown limit", *argv);
+               }
+               NEXT_ARG_FWD();
+       }
+
+       if (set_rcv_add_addrs)
+               addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_RCV_ADD_ADDRS,
+                         rcv_add_addrs);
+       if (set_subflows)
+               addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_SUBFLOWS, subflows);
+       return set_rcv_add_addrs || set_subflows;
+}
+
+static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
+{
+       struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
+       struct genlmsghdr *ghdr;
+       int len = n->nlmsg_len;
+       __u32 val;
+
+       if (n->nlmsg_type != genl_family)
+               return 0;
+
+       len -= NLMSG_LENGTH(GENL_HDRLEN);
+       if (len < 0)
+               return -1;
+
+       ghdr = NLMSG_DATA(n);
+       parse_rtattr(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
+
+       open_json_object(NULL);
+       if (tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]) {
+               val = rta_getattr_u32(tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]);
+
+               print_uint(PRINT_ANY, "add_addr_accepted",
+                          "add_addr_accepted %d ", val);
+       }
+
+       if (tb[MPTCP_PM_ATTR_SUBFLOWS]) {
+               val = rta_getattr_u32(tb[MPTCP_PM_ATTR_SUBFLOWS]);
+
+               print_uint(PRINT_ANY, "subflows", "subflows %d ", val);
+       }
+       print_string(PRINT_FP, NULL, "%s", "\n");
+       fflush(stdout);
+       close_json_object();
+       return 0;
+}
+
+static int mptcp_limit_get_set(int argc, char **argv, int cmd)
+{
+       bool do_get = cmd == MPTCP_PM_CMD_GET_LIMITS;
+       MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
+       struct nlmsghdr *answer;
+       int ret;
+
+       ret = mptcp_parse_limit(argc, argv, &req.n);
+       if (ret < 0)
+               return -1;
+
+       if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0)
+               return -2;
+
+       if (do_get)
+               return print_mptcp_limit(answer, stdout);
+       return 0;
+}
+
+int do_mptcp(int argc, char **argv)
+{
+       if (argc == 0)
+               usage();
+
+       if (matches(*argv, "help") == 0)
+               usage();
+
+       if (genl_init_handle(&genl_rth, MPTCP_PM_NAME, &genl_family))
+               exit(1);
+
+       if (matches(*argv, "endpoint") == 0) {
+               NEXT_ARG_FWD();
+               if (argc == 0)
+                       return mptcp_addr_show(0, NULL);
+
+               if (matches(*argv, "add") == 0)
+                       return mptcp_addr_modify(argc-1, argv+1,
+                                                MPTCP_PM_CMD_ADD_ADDR);
+               if (matches(*argv, "delete") == 0)
+                       return mptcp_addr_modify(argc-1, argv+1,
+                                                MPTCP_PM_CMD_DEL_ADDR);
+               if (matches(*argv, "show") == 0)
+                       return mptcp_addr_show(argc-1, argv+1);
+               if (matches(*argv, "flush") == 0)
+                       return mptcp_addr_flush(argc-1, argv+1);
+
+               goto unknown;
+       }
+
+       if (matches(*argv, "limits") == 0) {
+               NEXT_ARG_FWD();
+               if (argc == 0)
+                       return mptcp_limit_get_set(0, NULL,
+                                                  MPTCP_PM_CMD_GET_LIMITS);
+
+               if (matches(*argv, "set") == 0)
+                       return mptcp_limit_get_set(argc-1, argv+1,
+                                                  MPTCP_PM_CMD_SET_LIMITS);
+               if (matches(*argv, "show") == 0)
+                       return mptcp_limit_get_set(argc-1, argv+1,
+                                                  MPTCP_PM_CMD_GET_LIMITS);
+       }
+
+unknown:
+       fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
+               *argv);
+       exit(-1);
+}
index 07c4516943cc9ebe95933138e7af707965f9c6eb..05ec2c296579f162a6f5a553e1ee0037c4104221 100644 (file)
@@ -101,7 +101,7 @@ static void usage(void)
                "TIME := NUMBER[s|ms]\n"
                "BOOL := [1|0]\n"
                "FEATURES := ecn\n"
-               "ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local ]\n"
+               "ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl ]\n"
                "ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"
                "SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
                "SEGMODE := [ encap | inline ]\n"
index 0d7d7149b077547c98ec1bb14c0d5f251c6ef92a..9b4f0885b1e7667211264df47112182a432512dc 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <linux/seg6.h>
 #include <linux/seg6_iptunnel.h>
+#include <linux/rpl.h>
+#include <linux/rpl_iptunnel.h>
 #include <linux/seg6_hmac.h>
 #include <linux/seg6_local.h>
 #include <linux/if_tunnel.h>
@@ -50,6 +52,8 @@ static const char *format_encap_type(int type)
                return "seg6";
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                return "seg6local";
+       case LWTUNNEL_ENCAP_RPL:
+               return "rpl";
        default:
                return "unknown";
        }
@@ -84,6 +88,8 @@ static int read_encap_type(const char *name)
                return LWTUNNEL_ENCAP_SEG6;
        else if (strcmp(name, "seg6local") == 0)
                return LWTUNNEL_ENCAP_SEG6_LOCAL;
+       else if (strcmp(name, "rpl") == 0)
+               return LWTUNNEL_ENCAP_RPL;
        else if (strcmp(name, "help") == 0)
                encap_type_usage();
 
@@ -162,6 +168,42 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap)
        print_srh(fp, tuninfo->srh);
 }
 
+static void print_rpl_srh(FILE *fp, struct ipv6_rpl_sr_hdr *srh)
+{
+       int i;
+
+       if (is_json_context())
+               open_json_array(PRINT_JSON, "segs");
+       else
+               fprintf(fp, "segs %d [ ", srh->segments_left);
+
+       for (i = srh->segments_left - 1; i >= 0; i--) {
+               print_color_string(PRINT_ANY, COLOR_INET6,
+                                  NULL, "%s ",
+                                  rt_addr_n2a(AF_INET6, 16, &srh->rpl_segaddr[i]));
+       }
+
+       if (is_json_context())
+               close_json_array(PRINT_JSON, NULL);
+       else
+               fprintf(fp, "] ");
+}
+
+static void print_encap_rpl(FILE *fp, struct rtattr *encap)
+{
+       struct rtattr *tb[RPL_IPTUNNEL_MAX + 1];
+       struct ipv6_rpl_sr_hdr *srh;
+
+       parse_rtattr_nested(tb, RPL_IPTUNNEL_MAX, encap);
+
+       if (!tb[RPL_IPTUNNEL_SRH])
+               return;
+
+       srh = RTA_DATA(tb[RPL_IPTUNNEL_SRH]);
+
+       print_rpl_srh(fp, srh);
+}
+
 static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
        [SEG6_LOCAL_ACTION_END]                 = "End",
        [SEG6_LOCAL_ACTION_END_X]               = "End.X",
@@ -294,6 +336,110 @@ static void print_encap_mpls(FILE *fp, struct rtattr *encap)
                        rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
 }
 
+static void lwtunnel_print_geneve_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "geneve_opts";
+       int data_len, offset = 0;
+       char data[rem * 2 + 1];
+       __u16 class;
+       __u8 type;
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+
+       while (rem) {
+               parse_rtattr(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, i, rem);
+               class = rta_getattr_be16(tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]);
+               type = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]);
+               data_len = RTA_PAYLOAD(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]);
+               hexstring_n2a(RTA_DATA(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]),
+                             data_len, data, sizeof(data));
+               offset += data_len + 20;
+               rem -= data_len + 20;
+               i = RTA_DATA(attr) + offset;
+
+               open_json_object(NULL);
+               print_uint(PRINT_ANY, "class", "%u", class);
+               print_uint(PRINT_ANY, "type", ":%u", type);
+               if (rem)
+                       print_string(PRINT_ANY, "data", ":%s,", data);
+               else
+                       print_string(PRINT_ANY, "data", ":%s ", data);
+               close_json_object();
+       }
+
+       close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_vxlan_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "vxlan_opts";
+       __u32 gbp;
+
+       parse_rtattr(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, i, rem);
+       gbp = rta_getattr_u32(tb[LWTUNNEL_IP_OPT_VXLAN_GBP]);
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "gbp", "%u ", gbp);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_erspan_opts(struct rtattr *attr)
+{
+       struct rtattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       char *name = "erspan_opts";
+       __u8 ver, hwid, dir;
+       __u32 idx;
+
+       parse_rtattr(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, i, RTA_PAYLOAD(attr));
+       ver = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_VER]);
+       if (ver == 1) {
+               idx = rta_getattr_be32(tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]);
+               dir = 0;
+               hwid = 0;
+       } else {
+               idx = 0;
+               dir = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_DIR]);
+               hwid = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]);
+       }
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "ver", "%u", ver);
+       print_uint(PRINT_ANY, "index", ":%u", idx);
+       print_uint(PRINT_ANY, "dir", ":%u", dir);
+       print_uint(PRINT_ANY, "hwid", ":%u ", hwid);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+}
+
+static void lwtunnel_print_opts(struct rtattr *attr)
+{
+       struct rtattr *tb_opt[LWTUNNEL_IP_OPTS_MAX + 1];
+
+       parse_rtattr_nested(tb_opt, LWTUNNEL_IP_OPTS_MAX, attr);
+       if (tb_opt[LWTUNNEL_IP_OPTS_GENEVE])
+               lwtunnel_print_geneve_opts(tb_opt[LWTUNNEL_IP_OPTS_GENEVE]);
+       else if (tb_opt[LWTUNNEL_IP_OPTS_VXLAN])
+               lwtunnel_print_vxlan_opts(tb_opt[LWTUNNEL_IP_OPTS_VXLAN]);
+       else if (tb_opt[LWTUNNEL_IP_OPTS_ERSPAN])
+               lwtunnel_print_erspan_opts(tb_opt[LWTUNNEL_IP_OPTS_ERSPAN]);
+}
+
 static void print_encap_ip(FILE *fp, struct rtattr *encap)
 {
        struct rtattr *tb[LWTUNNEL_IP_MAX+1];
@@ -332,6 +478,9 @@ static void print_encap_ip(FILE *fp, struct rtattr *encap)
                if (flags & TUNNEL_SEQ)
                        print_bool(PRINT_ANY, "seq", "seq ", true);
        }
+
+       if (tb[LWTUNNEL_IP_OPTS])
+               lwtunnel_print_opts(tb[LWTUNNEL_IP_OPTS]);
 }
 
 static void print_encap_ila(FILE *fp, struct rtattr *encap)
@@ -404,6 +553,9 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap)
                if (flags & TUNNEL_SEQ)
                        print_bool(PRINT_ANY, "seq", "seq ", true);
        }
+
+       if (tb[LWTUNNEL_IP6_OPTS])
+               lwtunnel_print_opts(tb[LWTUNNEL_IP6_OPTS]);
 }
 
 static void print_encap_bpf(FILE *fp, struct rtattr *encap)
@@ -457,6 +609,9 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                print_encap_seg6local(fp, encap);
                break;
+       case LWTUNNEL_ENCAP_RPL:
+               print_encap_rpl(fp, encap);
+               break;
        }
 }
 
@@ -580,6 +735,79 @@ out:
        return ret;
 }
 
+static struct ipv6_rpl_sr_hdr *parse_rpl_srh(char *segbuf)
+{
+       struct ipv6_rpl_sr_hdr *srh;
+       int nsegs = 0;
+       int srhlen;
+       char *s;
+       int i;
+
+       s = segbuf;
+       for (i = 0; *s; *s++ == ',' ? i++ : *s);
+       nsegs = i + 1;
+
+       srhlen = 8 + 16 * nsegs;
+
+       srh = calloc(1, srhlen);
+
+       srh->hdrlen = (srhlen >> 3) - 1;
+       srh->type = 3;
+       srh->segments_left = nsegs;
+
+       for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) {
+               inet_prefix addr;
+
+               get_addr(&addr, s, AF_INET6);
+               memcpy(&srh->rpl_segaddr[i], addr.data, sizeof(struct in6_addr));
+               i--;
+       }
+
+       return srh;
+}
+
+static int parse_encap_rpl(struct rtattr *rta, size_t len, int *argcp,
+                          char ***argvp)
+{
+       struct ipv6_rpl_sr_hdr *srh;
+       char **argv = *argvp;
+       char segbuf[1024] = "";
+       int argc = *argcp;
+       int segs_ok = 0;
+       int ret = 0;
+       int srhlen;
+
+       while (argc > 0) {
+               if (strcmp(*argv, "segs") == 0) {
+                       NEXT_ARG();
+                       if (segs_ok++)
+                               duparg2("segs", *argv);
+
+                       strlcpy(segbuf, *argv, 1024);
+               } else {
+                       break;
+               }
+               argc--; argv++;
+       }
+
+       srh = parse_rpl_srh(segbuf);
+       srhlen = (srh->hdrlen + 1) << 3;
+
+       if (rta_addattr_l(rta, len, RPL_IPTUNNEL_SRH, srh,
+                         srhlen)) {
+               ret = -1;
+               goto out;
+       }
+
+       *argcp = argc + 1;
+       *argvp = argv - 1;
+
+out:
+       free(srh);
+
+       return ret;
+}
+
 struct lwt_x {
        struct rtattr *rta;
        size_t len;
@@ -798,11 +1026,189 @@ static int parse_encap_mpls(struct rtattr *rta, size_t len,
        return 0;
 }
 
+static int lwtunnel_parse_geneve_opt(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_GENEVE | NLA_F_NESTED);
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case LWTUNNEL_IP_OPT_GENEVE_CLASS:
+               {
+                       __be16 opt_class;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be16(&opt_class, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr16(rta, len, i, opt_class);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_GENEVE_TYPE:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr8(rta, len, i, opt_type);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_GENEVE_DATA:
+               {
+                       size_t token_len = strlen(token);
+                       __u8 *opts;
+
+                       if (!token_len)
+                               break;
+                       opts = malloc(token_len / 2);
+                       if (!opts)
+                               return -1;
+                       if (hex2mem(token, opts, token_len / 2) < 0) {
+                               free(opts);
+                               return -1;
+                       }
+                       rta_addattr_l(rta, len, i, opts, token_len / 2);
+                       free(opts);
+
+                       break;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+       rta_nest_end(rta, nest);
+
+       return 0;
+}
+
+static int lwtunnel_parse_geneve_opts(char *str, size_t len, struct rtattr *rta)
+{
+       char *token;
+       int err;
+
+       token = strsep(&str, ",");
+       while (token) {
+               err = lwtunnel_parse_geneve_opt(token, len, rta);
+               if (err)
+                       return err;
+
+               token = strsep(&str, ",");
+       }
+
+       return 0;
+}
+
+static int lwtunnel_parse_vxlan_opts(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       __u32 gbp;
+       int err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_VXLAN | NLA_F_NESTED);
+       err = get_u32(&gbp, str, 0);
+       if (err)
+               return err;
+       rta_addattr32(rta, len, LWTUNNEL_IP_OPT_VXLAN_GBP, gbp);
+
+       rta_nest_end(rta, nest);
+       return 0;
+}
+
+static int lwtunnel_parse_erspan_opts(char *str, size_t len, struct rtattr *rta)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_ERSPAN | NLA_F_NESTED);
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case LWTUNNEL_IP_OPT_ERSPAN_VER:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr8(rta, len, i, opt_type);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_ERSPAN_INDEX:
+               {
+                       __be32 opt_class;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be32(&opt_class, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr32(rta, len, i, opt_class);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_ERSPAN_DIR:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr8(rta, len, i, opt_type);
+                       break;
+               }
+               case LWTUNNEL_IP_OPT_ERSPAN_HWID:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       rta_addattr8(rta, len, i, opt_type);
+                       break;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+
+       rta_nest_end(rta, nest);
+       return 0;
+}
+
 static int parse_encap_ip(struct rtattr *rta, size_t len,
                          int *argcp, char ***argvp)
 {
        int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-       int key_ok = 0, csum_ok = 0, seq_ok = 0;
+       int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
@@ -854,6 +1260,51 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
                        if (get_u8(&ttl, *argv, 0))
                                invarg("\"ttl\" value is invalid\n", *argv);
                        ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
+               } else if (strcmp(*argv, "geneve_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"geneve_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "vxlan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"vxlan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "erspan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_erspan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"erspan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
                } else if (strcmp(*argv, "key") == 0) {
                        if (key_ok++)
                                duparg2("key", *argv);
@@ -969,7 +1420,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
                           int *argcp, char ***argvp)
 {
        int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
-       int key_ok = 0, csum_ok = 0, seq_ok = 0;
+       int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
        char **argv = *argvp;
        int argc = *argcp;
        int ret = 0;
@@ -1023,6 +1474,51 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
                                       *argv);
                        ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
                                           hoplimit);
+               } else if (strcmp(*argv, "geneve_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"geneve_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "vxlan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"vxlan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
+               } else if (strcmp(*argv, "erspan_opts") == 0) {
+                       struct rtattr *nest;
+
+                       if (opts_ok++)
+                               duparg2("opts", *argv);
+
+                       NEXT_ARG();
+
+                       nest = rta_nest(rta, len,
+                                       LWTUNNEL_IP_OPTS | NLA_F_NESTED);
+                       ret = lwtunnel_parse_erspan_opts(*argv, len, rta);
+                       if (ret)
+                               invarg("\"erspan_opts\" value is invalid\n",
+                                      *argv);
+                       rta_nest_end(rta, nest);
                } else if (strcmp(*argv, "key") == 0) {
                        if (key_ok++)
                                duparg2("key", *argv);
@@ -1159,6 +1655,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
        case LWTUNNEL_ENCAP_SEG6_LOCAL:
                ret = parse_encap_seg6local(rta, len, &argc, &argv);
                break;
+       case LWTUNNEL_ENCAP_RPL:
+               ret = parse_encap_rpl(rta, len, &argc, &argv);
+               break;
        default:
                fprintf(stderr, "Error: unsupported encap type\n");
                break;
index b9a43675cbd6cab6ddc338b747a914f830a6b20b..28dd8e255fce7f4fdae8554ec60b39081aac5124 100644 (file)
@@ -225,7 +225,7 @@ static int ipvrf_pids(int argc, char **argv)
                return -1;
        }
 
-       mnt = find_cgroup2_mount();
+       mnt = find_cgroup2_mount(true);
        if (!mnt)
                return -1;
 
@@ -366,7 +366,7 @@ static int vrf_switch(const char *name)
                }
        }
 
-       mnt = find_cgroup2_mount();
+       mnt = find_cgroup2_mount(true);
        if (!mnt)
                return -1;
 
index d616a970e9a28cf637326508373515616efc40c3..0461e5d06ef31c3ba7d3625f96838366da7b4af7 100644 (file)
@@ -354,8 +354,8 @@ get_failed:
                        NEXT_ARG();
                        if (get_u8(&erspan_ver, *argv, 0))
                                invarg("invalid erspan version\n", *argv);
-                       if (erspan_ver != 1 && erspan_ver != 2)
-                               invarg("erspan version must be 1 or 2\n", *argv);
+                       if (erspan_ver > 2)
+                               invarg("erspan version must be 0/1/2\n", *argv);
                } else if (is_erspan && strcmp(*argv, "erspan_dir") == 0) {
                        NEXT_ARG();
                        if (matches(*argv, "ingress") == 0)
index 94a4ee700431cac46af5be0ff4b10732551dd48e..9d270f4b44558dfcd09341fe424a2456bc2e73b9 100644 (file)
@@ -389,8 +389,8 @@ get_failed:
                        NEXT_ARG();
                        if (get_u8(&erspan_ver, *argv, 0))
                                invarg("invalid erspan version\n", *argv);
-                       if (erspan_ver != 1 && erspan_ver != 2)
-                               invarg("erspan version must be 1 or 2\n", *argv);
+                       if (erspan_ver > 2)
+                               invarg("erspan version must be 0/1/2\n", *argv);
                } else if (strcmp(*argv, "erspan_dir") == 0) {
                        NEXT_ARG();
                        if (matches(*argv, "ingress") == 0)
@@ -430,7 +430,7 @@ get_failed:
        addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
        addattr32(n, 1024, IFLA_GRE_FLAGS, flags);
        addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
-       if (erspan_ver) {
+       if (erspan_ver <= 2) {
                addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
                if (erspan_ver == 1 && erspan_idx != 0) {
                        addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
index bab8cbf510074bf9c68a8e4e74e35536fa2b9107..7cba1857b7fac517ccd04003547b01c9c20f15b5 100644 (file)
@@ -5,7 +5,7 @@ CFLAGS += -fPIC
 
 UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
        inet_proto.o namespace.o json_writer.o json_print.o \
-       names.o color.o bpf.o exec.o fs.o
+       names.o color.o bpf.o exec.o fs.o cg_map.o
 
 NLOBJ=libgenl.o libnetlink.o
 
diff --git a/lib/cg_map.c b/lib/cg_map.c
new file mode 100644 (file)
index 0000000..77f030e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * cg_map.c    cgroup v2 cache
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Authors:    Dmitry Yakunin <zeil@yandex-team.ru>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <linux/types.h>
+#include <linux/limits.h>
+#include <ftw.h>
+
+#include "cg_map.h"
+#include "list.h"
+#include "utils.h"
+
+struct cg_cache {
+       struct hlist_node id_hash;
+       __u64   id;
+       char    path[];
+};
+
+#define IDMAP_SIZE     1024
+static struct hlist_head id_head[IDMAP_SIZE];
+
+static struct cg_cache *cg_get_by_id(__u64 id)
+{
+       unsigned int h = id & (IDMAP_SIZE - 1);
+       struct hlist_node *n;
+
+       hlist_for_each(n, &id_head[h]) {
+               struct cg_cache *cg;
+
+               cg = container_of(n, struct cg_cache, id_hash);
+               if (cg->id == id)
+                       return cg;
+       }
+
+       return NULL;
+}
+
+static struct cg_cache *cg_entry_create(__u64 id, const char *path)
+{
+       unsigned int h = id & (IDMAP_SIZE - 1);
+       struct cg_cache *cg;
+
+       cg = malloc(sizeof(*cg) + strlen(path) + 1);
+       if (!cg) {
+               fprintf(stderr,
+                       "Failed to allocate memory for cgroup2 cache entry");
+               return NULL;
+       }
+       cg->id = id;
+       strcpy(cg->path, path);
+
+       hlist_add_head(&cg->id_hash, &id_head[h]);
+
+       return cg;
+}
+
+static int mntlen;
+
+static int nftw_fn(const char *fpath, const struct stat *sb,
+                  int typeflag, struct FTW *ftw)
+{
+       const char *path;
+       __u64 id;
+
+       if (typeflag != FTW_D)
+               return 0;
+
+       id = get_cgroup2_id(fpath);
+       if (!id)
+               return -1;
+
+       path = fpath + mntlen;
+       if (*path == '\0')
+               /* root cgroup */
+               path = "/";
+       if (!cg_entry_create(id, path))
+               return -1;
+
+       return 0;
+}
+
+static void cg_init_map(void)
+{
+       char *mnt;
+
+       mnt = find_cgroup2_mount(false);
+       if (!mnt)
+               exit(1);
+
+       mntlen = strlen(mnt);
+       if (nftw(mnt, nftw_fn, 1024, FTW_MOUNT) < 0)
+               exit(1);
+
+       free(mnt);
+}
+
+const char *cg_id_to_path(__u64 id)
+{
+       static int initialized;
+       static char buf[64];
+
+       const struct cg_cache *cg;
+       char *path;
+
+       if (!initialized) {
+               cg_init_map();
+               initialized = 1;
+       }
+
+       cg = cg_get_by_id(id);
+       if (cg)
+               return cg->path;
+
+       path = get_cgroup2_path(id, false);
+       if (path) {
+               cg = cg_entry_create(id, path);
+               free(path);
+               if (cg)
+                       return cg->path;
+       }
+
+       snprintf(buf, sizeof(buf), "unreachable:%llx", id);
+       return buf;
+}
index 86efd4ed2ed8084ad147b5d53e62ac2458ccf289..e265fc04eaec5ae62ce524500a5448145b2ecd54 100644 (file)
--- a/lib/fs.c
+++ b/lib/fs.c
@@ -59,13 +59,18 @@ static char *find_fs_mount(const char *fs_to_find)
 }
 
 /* caller needs to free string returned */
-char *find_cgroup2_mount(void)
+char *find_cgroup2_mount(bool do_mount)
 {
        char *mnt = find_fs_mount(CGROUP2_FS_NAME);
 
        if (mnt)
                return mnt;
 
+       if (!do_mount) {
+               fprintf(stderr, "Failed to find cgroup2 mount\n");
+               return NULL;
+       }
+
        mnt = strdup(MNT_CGRP2_PATH);
        if (!mnt) {
                fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
@@ -74,7 +79,7 @@ char *find_cgroup2_mount(void)
        }
 
        if (make_path(mnt, 0755)) {
-               fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
+               fprintf(stderr, "Failed to setup cgroup2 directory\n");
                free(mnt);
                return NULL;
        }
@@ -99,6 +104,134 @@ out:
        return mnt;
 }
 
+__u64 get_cgroup2_id(const char *path)
+{
+       char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
+       struct file_handle *fhp = (struct file_handle *)fh_buf;
+       union {
+               __u64 id;
+               unsigned char bytes[sizeof(__u64)];
+       } cg_id = { .id = 0 };
+       char *mnt = NULL;
+       int mnt_fd = -1;
+       int mnt_id;
+
+       if (!path) {
+               fprintf(stderr, "Invalid cgroup2 path\n");
+               return 0;
+       }
+
+       fhp->handle_bytes = sizeof(__u64);
+       if (name_to_handle_at(AT_FDCWD, path, fhp, &mnt_id, 0) < 0) {
+               /* try at cgroup2 mount */
+
+               while (*path == '/')
+                       path++;
+               if (*path == '\0') {
+                       fprintf(stderr, "Invalid cgroup2 path\n");
+                       goto out;
+               }
+
+               mnt = find_cgroup2_mount(false);
+               if (!mnt)
+                       goto out;
+
+               mnt_fd = open(mnt, O_RDONLY);
+               if (mnt_fd < 0) {
+                       fprintf(stderr, "Failed to open cgroup2 mount\n");
+                       goto out;
+               }
+
+               fhp->handle_bytes = sizeof(__u64);
+               if (name_to_handle_at(mnt_fd, path, fhp, &mnt_id, 0) < 0) {
+                       fprintf(stderr, "Failed to get cgroup2 ID: %s\n",
+                                       strerror(errno));
+                       goto out;
+               }
+               if (fhp->handle_bytes != sizeof(__u64)) {
+                       fprintf(stderr, "Invalid size of cgroup2 ID\n");
+                       goto out;
+               }
+       }
+
+       memcpy(cg_id.bytes, fhp->f_handle, sizeof(__u64));
+
+out:
+       close(mnt_fd);
+       free(mnt);
+
+       return cg_id.id;
+}
+
+#define FILEID_INO32_GEN 1
+
+/* caller needs to free string returned */
+char *get_cgroup2_path(__u64 id, bool full)
+{
+       char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
+       struct file_handle *fhp = (struct file_handle *)fh_buf;
+       union {
+               __u64 id;
+               unsigned char bytes[sizeof(__u64)];
+       } cg_id = { .id = id };
+       int mnt_fd = -1, fd = -1;
+       char link_buf[PATH_MAX];
+       char *path = NULL;
+       char fd_path[64];
+       int link_len;
+       char *mnt;
+
+       if (!id) {
+               fprintf(stderr, "Invalid cgroup2 ID\n");
+               return NULL;
+       }
+
+       mnt = find_cgroup2_mount(false);
+       if (!mnt)
+               return NULL;
+
+       mnt_fd = open(mnt, O_RDONLY);
+       if (mnt_fd < 0) {
+               fprintf(stderr, "Failed to open cgroup2 mount\n");
+               goto out;
+       }
+
+       fhp->handle_bytes = sizeof(__u64);
+       fhp->handle_type = FILEID_INO32_GEN;
+       memcpy(fhp->f_handle, cg_id.bytes, sizeof(__u64));
+
+       fd = open_by_handle_at(mnt_fd, fhp, 0);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to open cgroup2 by ID\n");
+               goto out;
+       }
+
+       snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
+       link_len = readlink(fd_path, link_buf, sizeof(link_buf) - 1);
+       if (link_len < 0) {
+               fprintf(stderr,
+                       "Failed to read value of symbolic link %s\n",
+                       fd_path);
+               goto out;
+       }
+       link_buf[link_len] = '\0';
+
+       if (full)
+               path = strdup(link_buf);
+       else
+               path = strdup(link_buf + strlen(mnt));
+       if (!path)
+               fprintf(stderr,
+                       "Failed to allocate memory for cgroup2 path\n");
+
+out:
+       close(fd);
+       close(mnt_fd);
+       free(mnt);
+
+       return path;
+}
+
 int make_path(const char *path, mode_t mode)
 {
        char *dir, *delim;
index 70a86cf0acdc20e6b4b105710c0dad9fa586f60d..8a3c77be737be17dd869c4f3e86d7b5b985ab452 100644 (file)
@@ -58,6 +58,9 @@ devlink-health \- devlink health reporting and recovery
 .RI "[ "
 .BR auto_recover " { " true " | " false " } "
 .RI "]"
+.RI "[ "
+.BR auto_dump " { " true " | " false " } "
+.RI "]"
 
 .ti -8
 .B devlink health help
@@ -131,8 +134,8 @@ the next "devlink health dump show" command.
 - specifies the reporter's name registered on the devlink device.
 
 .SS devlink health set - Configure health reporter.
-Please note that this command is not supported on a reporter which
-doesn't support a recovery method.
+Please note that some params are not supported on a reporter which
+doesn't support a recovery or dump method.
 
 .PP
 .I "DEV"
@@ -150,6 +153,10 @@ Time interval between consecutive auto recoveries.
 .BR auto_recover " { " true " | " false " } "
 Indicates whether the devlink should execute automatic recover on error.
 
+.TP
+.BR auto_dump " { " true " | " false " } "
+Indicates whether the devlink should execute automatic dump on error.
+
 .SH "EXAMPLES"
 .PP
 devlink health show
index 2a553190a37e994e1ecd88d53471268c36404ca1..fe773c91592fb767d2c1c233798f9dc823ad7c88 100644 (file)
@@ -92,7 +92,7 @@ ip-address \- protocol address management
 
 .ti -8
 .IR CONFFLAG " := "
-.RB "[ " home " | " mngtmpaddr " | " nodad " | " noprefixroute " | " autojoin " ]"
+.RB "[ " home " | " mngtmpaddr " | " nodad " | " optimstic " | " noprefixroute " | " autojoin " ]"
 
 .ti -8
 .IR LIFETIME " := [ "
@@ -258,6 +258,11 @@ stateless auto-configuration was active.
 (IPv6 only) do not perform Duplicate Address Detection (RFC 4862) when
 adding this address.
 
+.TP
+.B optimistic
+(IPv6 only) When performing Duplicate Address Detection, use the RFC 4429
+optimistic variant.
+
 .TP
 .B noprefixroute
 Do not automatically create a route for the network prefix of the added
index 939e2ad49f4e8c58cb5b53fc4d6c2357f01e5fdf..e8a25451f7cd4a4cdbf5497992be1dcb340d5db0 100644 (file)
@@ -1163,8 +1163,8 @@ It must be an address on another interface on this host.
 .BR erspan_ver " \fIversion "
 - specifies the ERSPAN version number.
 .IR version
-indicates the ERSPAN version to be created: 1 for version 1 (type II)
-or 2 for version 2 (type III).
+indicates the ERSPAN version to be created: 0 for version 0 type I,
+1 for version 1 (type II) or 2 for version 2 (type III).
 
 .sp
 .BR erspan " \fIIDX "
index d5f9d240bf1241c730b2e0d6e15fbcb4051131f6..8e9175c57eff021b89ce348f294ec1404244e0b8 100644 (file)
@@ -23,6 +23,8 @@ ip-macsec \- MACsec device configuration
 ] [
 .BR validate " { " strict " | " check " | " disabled " } ] ["
 .BI encodingsa " SA"
+] [
+.BR offload " { " off " | " phy " | " mac " }"
 ]
 
 .BI "ip macsec add " DEV " tx sa"
@@ -54,7 +56,7 @@ ip-macsec \- MACsec device configuration
 .RI "{ " 0..3 " }"
 
 .BI "ip macsec offload " DEV
-.RB "{ " off " | " phy " }"
+.RB "{ " off " | " phy " | " mac " }"
 
 .B ip macsec show
 .RI [ " DEV " ]
@@ -86,7 +88,7 @@ type.
 
 .SH EXAMPLES
 .PP
-.SS Create a MACsec device on link eth0
+.SS Create a MACsec device on link eth0 (offload is disabled by default)
 .nf
 # ip link add link eth0 macsec0 type macsec port 11 encrypt on
 .PP
@@ -109,6 +111,10 @@ type.
 .SS Configure offloading on an interface
 .nf
 # ip macsec offload macsec0 phy
+.PP
+.SS Configure offloading upon MACsec device creation
+.nf
+# ip link add link eth0 macsec0 type macsec port 11 encrypt on offload mac
 
 .SH NOTES
 This tool can be used to configure the 802.1AE keys of the interface. Note that 802.1AE uses GCM-AES
diff --git a/man/man8/ip-mptcp.8 b/man/man8/ip-mptcp.8
new file mode 100644 (file)
index 0000000..ef8409e
--- /dev/null
@@ -0,0 +1,141 @@
+.TH IP\-MPTCP 8 "4 Apr 2020" "iproute2" "Linux"
+.SH "NAME"
+ip-mptcp \- MPTCP path manager configuration
+.SH "SYNOPSIS"
+.ad l
+.in +8
+.ti -8
+.B ip
+.RI "[ " OPTIONS " ]"
+.B mptcp
+.RB "{ "
+.B endpoint
+.RB " | "
+.B limits
+.RB " | "
+.B help
+.RB " }"
+.sp
+
+.ti -8
+.BR "ip mptcp endpoint add "
+.IR IFADDR
+.RB "[ " dev
+.IR IFNAME " ]"
+.RB "[ " id
+.I ID
+.RB "] [ "
+.I FLAG-LIST
+.RB "] "
+
+.ti -8
+.BR "ip mptcp endpoint del id "
+.I ID
+
+.ti -8
+.BR "ip mptcp endpoint show "
+.RB "[ " id
+.I ID
+.RB "]"
+
+.ti -8
+.BR "ip mptcp endpoint flush"
+
+.ti -8
+.IR FLAG-LIST " := [ "  FLAG-LIST " ] " FLAG
+
+.ti -8
+.IR FLAG " := ["
+.B signal
+.RB "|"
+.B subflow
+.RB "|"
+.B backup
+.RB  "]"
+
+.ti -8
+.BR "ip mptcp limits set "
+.RB "[ "
+.B subflow
+.IR SUBFLOW_NR " ]"
+.RB "[ "
+.B add_addr_accepted
+.IR  ADD_ADDR_ACCEPTED_NR " ]"
+
+.ti -8
+.BR "ip mptcp limits show"
+
+.SH DESCRIPTION
+
+MPTCP is a transport protocol built on top of TCP that allows TCP
+connections to use multiple paths to maximize resource usage and increase
+redundancy. The ip-mptcp sub-commands allow configuring several aspects of the
+MPTCP path manager, which is in charge of subflows creation:
+
+.P
+The
+.B endpoint
+object specifies the IP addresses that will be used and/or announced for
+additional subflows:
+
+.TS
+l l.
+ip mptcp endpoint add  add new MPTCP endpoint
+ip mptcp endpoint delete       delete existing MPTCP endpoint
+ip mptcp endpoint show get existing MPTCP endpoint
+ip mptcp endpoint flush        flush all existing MPTCP endpoints
+.TE
+
+.TP
+.IR ID
+is a unique numeric identifier for the given endpoint
+
+.TP
+.BR signal
+the endpoint will be announced/signalled to each peer via an ADD_ADDR MPTCP
+sub-option
+
+.TP
+.BR subflow
+if additional subflow creation is allowed by MPTCP limits, the endpoint will
+be used as the source address to create an additional subflow after that
+the MPTCP connection is established.
+
+.TP
+.BR backup
+the endpoint will be announced as a backup address, if this is a
+.BR signal
+endpoint, or the subflow will be created as a backup one if this is a
+.BR subflow
+endpoint
+
+.sp
+.PP
+The
+.B limits
+object specifies the constraints for subflow creations:
+
+.TS
+l l.
+ip mptcp limits show   get current MPTCP subflow creation limits
+ip mptcp limits set    change the MPTCP subflow creation limits
+.TE
+
+.TP
+.IR SUBFLOW_NR
+specifies the maximum number of additional subflows allowed for each MPTCP
+connection. Additional subflows can be created due to: incoming accepted
+ADD_ADDR option, local
+.BR subflow
+endpoints, additional subflows started by the peer.
+
+.TP
+.IR ADD_ADDR_ACCEPTED_NR
+specifies the maximum number of ADD_ADDR suboptions accepted for each MPTCP
+connection. The MPTCP path manager will try to create a new subflow for
+each accepted ADD_ADDR option, respecting the
+.IR SUBFLOW_NR
+limit.
+
+.SH AUTHOR
+Original Manpage by Paolo Abeni <pabeni@redhat.com>
index 1613f790a14b21233833f000140bf061fbfe2b5a..c9f7671e591f070aa94079438fd2984086cb70e2 100644 (file)
@@ -22,7 +22,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
 .BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
  ntable " | " tunnel " | " tuntap " | " maddress " | "  mroute " | " mrule " | "\
  monitor " | " xfrm " | " netns " | "  l2tp " | "  tcp_metrics " | " token " | "\
- macsec " | " vrf " }"
+ macsec " | " vrf " | " mptcp " }"
 .sp
 
 .ti -8
@@ -268,6 +268,10 @@ readability.
 .B monitor
 - watch for netlink messages.
 
+.TP
+.B mptcp
+- manage MPTCP path manager.
+
 .TP
 .B mroute
 - multicast routing cache entry.
@@ -405,6 +409,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
 .BR ip-link (8),
 .BR ip-maddress (8),
 .BR ip-monitor (8),
+.BR ip-mptcp (8),
 .BR ip-mroute (8),
 .BR ip-neighbour (8),
 .BR ip-netns (8),
index 023d771b17878091d036159d6b796e931f44a233..3b2559ff7e77de2581939f2c639ea6cd15b803a2 100644 (file)
@@ -261,6 +261,11 @@ the pacing rate and max pacing rate
 .TP
 .B rcv_space:<rcv_space>
 a helper variable for TCP internal auto tuning socket receive buffer
+.P
+.TP
+.B tcp-ulp-mptcp flags:[MmBbJjecv] token:<rem_token(rem_id)/loc_token(loc_id)> seq:<sn> sfseq:<ssn> ssnoff:<off> maplen:<maplen>
+MPTCP subflow information
+.P
 .RE
 .TP
 .B \-\-tos
@@ -281,6 +286,15 @@ Class id set by net_cls cgroup. If class is zero this shows priority
 set by SO_PRIORITY.
 .RE
 .TP
+.B \-\-cgroup
+Show cgroup information. Below fields may appear:
+.RS
+.P
+.TP
+.B cgroup
+Cgroup v2 pathname. This pathname is relative to the mount point of the hierarchy.
+.RE
+.TP
 .B \-K, \-\-kill
 Attempts to forcibly close sockets. This option displays sockets that are
 successfully closed and silently skips sockets that the kernel does not support
index 12df48dc060abe365316c08e332d91fc85928309..4d32ff1b5c9a502a3d78da0a15511c3dfdf39250 100644 (file)
@@ -87,7 +87,13 @@ flower \- flow based traffic control filter
 .IR TOS " | "
 .B enc_ttl
 .IR TTL " | "
+{
 .B geneve_opts
+|
+.B vxlan_opts
+|
+.B erspan_opts
+}
 .IR OPTIONS " | "
 .BR ip_flags
 .IR IP_FLAGS
@@ -332,6 +338,10 @@ Match the connection zone, and can be masked.
 .RE
 .TP
 .BI geneve_opts " OPTIONS"
+.TQ
+.BI vxlan_opts " OPTIONS"
+.TQ
+.BI erspan_opts " OPTIONS"
 Match on IP tunnel metadata. Key id
 .I NUMBER
 is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
@@ -352,6 +362,21 @@ the masks is missing, \fBtc\fR assumes a full-length match. The options can
 be described in the form CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK,
 where CLASS is represented as a 16bit hexadecimal value, TYPE as an 8bit
 hexadecimal value and DATA as a variable length hexadecimal value.
+vxlan_opts
+.I OPTIONS
+doesn't support multiple options, and it consists of a key followed by a slash
+and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
+match. The option can be described in the form GBP/GBP_MASK, where GBP is
+represented as a 32bit number.
+erspan_opts
+.I OPTIONS
+doesn't support multiple options, and it consists of a key followed by a slash
+and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
+match. The option can be described in the form
+VERSION:INDEX:DIR:HWID/VERSION:INDEX_MASK:DIR_MASK:HWID_MASK, where VERSION is
+represented as a 8bit number, INDEX as an 32bit number, DIR and HWID as a 8bit
+number. Multiple options is not supported. Note INDEX/INDEX_MASK is used when
+VERSION is 1, and DIR/DIR_MASK and HWID/HWID_MASK are used when VERSION is 2.
 .TP
 .BI ip_flags " IP_FLAGS"
 .I IP_FLAGS
diff --git a/man/man8/tc-gate.8 b/man/man8/tc-gate.8
new file mode 100644 (file)
index 0000000..23d93ca
--- /dev/null
@@ -0,0 +1,123 @@
+.TH GATE 8 "12 Mar 2020" "iproute2" "Linux"
+.SH NAME
+gate \- Stream Gate Action
+.SH SYNOPSIS
+.B tc " ... " action gate
+.ti +8
+.B [ base-time
+BASETIME ]
+.B [ clockid
+CLOCKID ]
+.ti +8
+.B sched-entry
+<gate state> <interval 1> [ <internal priority> <max octets> ]
+.ti +8
+.B sched-entry
+<gate state> <interval 2> [ <internal priority> <max octets> ]
+.ti +8
+.B sched-entry
+<gate state> <interval 3> [ <internal priority> <max octets> ]
+.ti +8
+.B ......
+.ti +8
+.B sched-entry
+<gate state> <interval N> [ <internal priority> <max octets> ]
+
+.SH DESCRIPTION
+GATE action allows specified ingress frames can be passed at
+specific time slot, or be dropped at specific time slot. Tc filter
+filters the ingress frames, then tc gate action would specify which time
+slot and how many bytes these frames can be passed to device and
+which time slot frames would be dropped.
+Gate action also assign a base-time to tell when the entry list start.
+Then gate action would start to repeat the gate entry list cyclically
+at the start base-time.
+For the software simulation, gate action requires the user assign reference
+time clock type.
+
+.SH PARAMETERS
+
+.TP
+base-time
+.br
+Specifies the instant in nanoseconds, defining the time when the schedule
+starts. If 'base-time' is a time in the past, the schedule will start at
+
+base-time + (N * cycle-time)
+
+where N is the smallest integer so the resulting time is greater than
+"now", and "cycle-time" is the sum of all the intervals of the entries
+in the schedule. Without base-time specified, will default to be 0.
+
+.TP
+clockid
+.br
+Specifies the clock to be used by qdisc's internal timer for measuring
+time and scheduling events. Not valid if gate action is used for offloading
+filter.
+For example, tc filter command with
+.B skip_sw
+parameter.
+
+.TP
+sched-entry
+.br
+There may multiple
+.B sched-entry
+parameters in a single schedule. Each one has the format:
+
+sched-entry <gate state> <interval> [ <internal priority> <max octets> ]
+
+.br
+<gate state> means gate states. 'open' keep gate open, 'close' keep gate close.
+.br
+<interval> means how much nano seconds for this time slot.
+.br
+<internal priority> means internal priority value. Present of the
+internal receiving queue for this stream. "-1" means wildcard.
+<internal priority> and <max octets> can be omit default to be "-1" which both
+ value to be "-1" for this <sched-entry>.
+.br
+<max octets> means how many octets size could pass in this time slot. Dropped
+if overlimited. "-1" means wildcard. <max octets> can be omit default to be
+"-1" which value to be "-1" for this <sched-entry>.
+.br
+Note that <internal priority> and <max octets> are nothing meaning for gate state
+is "close" in a "sched-entry". All frames are dropped when "sched-entry" with
+"close" state.
+
+.SH EXAMPLES
+
+The following example shows tc filter frames source ip match to the
+192.168.0.20 will keep the gate open for 200ms and limit the traffic to 8MB
+in this sched-entry. Then keep the traffic gate to be close for 100ms.
+Frames arrived at gate close state would be dropped. Then the cycle would
+run the gate entries periodically. The schedule will start at instant 200.0s
+using the reference CLOCK_TAI. The schedule is composed of two entries
+each of 300ms duration.
+
+.EX
+# tc qdisc add dev eth0 ingress
+# tc filter add dev eth0 parent ffff: protocol ip \\
+           flower skip_hw src_ip 192.168.0.20 \\
+           action gate index 2 clockid CLOCK_TAI \\
+           base-time 200000000000ns \\
+           sched-entry open 200000000ns -1 8000000b \\
+           sched-entry close 100000000ns
+
+.EE
+
+Following commands is an example to filter a stream source mac match to the
+10:00:80:00:00:00 icmp frames will be dropped at any time with cycle 200ms.
+With a default basetime 0 and clockid is CLOCK_TAI as default.
+
+.EX
+# tc qdisc add dev eth0 ingress
+# tc filter add dev eth0 parent ffff:  protocol ip \\
+       flower ip_proto icmp dst_mac 10:00:80:00:00:00 \\
+       action gate index 12 sched-entry close 200000000ns
+
+.EE
+
+.SH AUTHORS
+Po Liu <Po.Liu@nxp.com>
index 2145eb62e70e275b6670f68ed666ad8bc32c90ad..ad9972402c0e53efe85b02d0461aeb3398ad5a7f 100644 (file)
@@ -66,8 +66,12 @@ options.
 .B id
 ,
 .B dst_port
-and
+,
 .B geneve_opts
+,
+.B vxlan_opts
+and
+.B erspan_opts
 are optional.
 .RS
 .TP
@@ -91,6 +95,20 @@ is specified in the form CLASS:TYPE:DATA, where CLASS is represented as a
 variable length hexadecimal value. Additionally multiple options may be
 listed using a comma delimiter.
 .TP
+.B vxlan_opts
+Vxlan metatdata options.
+.B vxlan_opts
+is specified in the form GBP, as a 32bit number. Multiple options is not
+supported.
+.TP
+.B erspan_opts
+Erspan metatdata options.
+.B erspan_opts
+is specified in the form VERSION:INDEX:DIR:HWID, where VERSION is represented
+as a 8bit number, INDEX as an 32bit number, DIR and HWID as a 8bit number.
+Multiple options is not supported. Note INDEX is used when VERSION is 1,
+and DIR and HWID are used when VERSION is 2.
+.TP
 .B tos
 Outer header TOS
 .TP
index 1debfb15986f5ec3b704297ffaeeeb24cb219bd2..50dae79c7de1b486243bca9b6247e530e09b9ab5 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-SSOBJ=ss.o ssfilter.tab.o
+SSOBJ=ss.o ssfilter_check.o ssfilter.tab.o
 LNSTATOBJ=lnstat.o lnstat_util.o
 
 TARGETS=ss nstat ifstat rtacct lnstat
index 75fde231201ddb534e937114286612e2d687cff4..f3d01812cb06d040e0ecdfd8fae9d8dd2872607f 100644 (file)
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -29,6 +29,7 @@
 #include <limits.h>
 #include <stdarg.h>
 
+#include "ss_util.h"
 #include "utils.h"
 #include "rt_names.h"
 #include "ll_map.h"
 #include "namespace.h"
 #include "SNAPSHOT.h"
 #include "rt_names.h"
+#include "cg_map.h"
 
 #include <linux/tcp.h>
-#include <linux/sock_diag.h>
-#include <linux/inet_diag.h>
 #include <linux/unix_diag.h>
 #include <linux/netdevice.h>   /* for MAX_ADDR_LEN */
 #include <linux/filter.h>
@@ -53,6 +53,7 @@
 #include <linux/tipc_netlink.h>
 #include <linux/tipc_sockets_diag.h>
 #include <linux/tls.h>
+#include <linux/mptcp.h>
 
 /* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */
 #ifndef PF_VSOCK
 #define AF_VSOCK PF_VSOCK
 #endif
 
-#define MAGIC_SEQ 123456
 #define BUF_CHUNK (1024 * 1024)        /* Buffer chunk allocation size */
 #define BUF_CHUNKS_MAX 5       /* Maximum number of allocated buffer chunks */
 #define LEN_ALIGN(x) (((x) + 1) & ~1)
 
-#define DIAG_REQUEST(_req, _r)                                             \
-       struct {                                                            \
-               struct nlmsghdr nlh;                                        \
-               _r;                                                         \
-       } _req = {                                                          \
-               .nlh = {                                                    \
-                       .nlmsg_type = SOCK_DIAG_BY_FAMILY,                  \
-                       .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\
-                       .nlmsg_seq = MAGIC_SEQ,                             \
-                       .nlmsg_len = sizeof(_req),                          \
-               },                                                          \
-       }
-
 #if HAVE_SELINUX
 #include <selinux/selinux.h>
 #else
@@ -122,6 +109,7 @@ static int follow_events;
 static int sctp_ino;
 static int show_tipcinfo;
 static int show_tos;
+static int show_cgroup;
 int oneline;
 
 enum col_id {
@@ -797,6 +785,7 @@ struct sockstat {
        char *name;
        char *peer_name;
        __u32               mark;
+       __u64               cgroup_id;
 };
 
 struct dctcpstat {
@@ -1417,6 +1406,9 @@ static void sock_details_print(struct sockstat *s)
 
        if (s->mark)
                out(" fwmark:0x%x", s->mark);
+
+       if (s->cgroup_id)
+               out(" cgroup:%s", cg_id_to_path(s->cgroup_id));
 }
 
 static void sock_addr_print(const char *addr, char *delim, const char *port,
@@ -1643,6 +1635,7 @@ struct aafilter {
        unsigned int    iface;
        __u32           mark;
        __u32           mask;
+       __u64           cgroup_id;
        struct aafilter *next;
 };
 
@@ -1770,6 +1763,12 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s)
                struct aafilter *a = (void *)f->pred;
 
                return (s->mark & a->mask) == a->mark;
+       }
+               case SSF_CGROUPCOND:
+       {
+               struct aafilter *a = (void *)f->pred;
+
+               return s->cgroup_id == a->cgroup_id;
        }
                /* Yup. It is recursion. Sorry. */
                case SSF_AND:
@@ -1961,6 +1960,23 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
                        { a->mark, a->mask},
                };
 
+               return inslen;
+       }
+               case SSF_CGROUPCOND:
+       {
+               struct aafilter *a = (void *)f->pred;
+               struct instr {
+                       struct inet_diag_bc_op op;
+                       __u64 cgroup_id;
+               } __attribute__((packed));
+               int inslen = sizeof(struct instr);
+
+               if (!(*bytecode = malloc(inslen))) abort();
+               ((struct instr *)*bytecode)[0] = (struct instr) {
+                       { INET_DIAG_BC_CGROUP_COND, inslen, inslen + 4 },
+                       a->cgroup_id,
+               };
+
                return inslen;
        }
                default:
@@ -2300,6 +2316,22 @@ void *parse_markmask(const char *markmask)
        return res;
 }
 
+void *parse_cgroupcond(const char *path)
+{
+       struct aafilter *res;
+       __u64 id;
+
+       id = get_cgroup2_id(path);
+       if (!id)
+               return NULL;
+
+       res = malloc(sizeof(*res));
+       if (res)
+               res->cgroup_id = id;
+
+       return res;
+}
+
 static void proc_ctx_print(struct sockstat *s)
 {
        char *buf;
@@ -2845,6 +2877,59 @@ static void tcp_tls_conf(const char *name, struct rtattr *attr)
        }
 }
 
+static void mptcp_subflow_info(struct rtattr *tb[])
+{
+       u_int32_t flags = 0;
+
+       if (tb[MPTCP_SUBFLOW_ATTR_FLAGS]) {
+               char caps[32 + 1] = { 0 }, *cap = &caps[0];
+
+               flags = rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_FLAGS]);
+
+               if (flags & MPTCP_SUBFLOW_FLAG_MCAP_REM)
+                       *cap++ = 'M';
+               if (flags & MPTCP_SUBFLOW_FLAG_MCAP_LOC)
+                       *cap++ = 'm';
+               if (flags & MPTCP_SUBFLOW_FLAG_JOIN_REM)
+                       *cap++ = 'J';
+               if (flags & MPTCP_SUBFLOW_FLAG_JOIN_LOC)
+                       *cap++ = 'j';
+               if (flags & MPTCP_SUBFLOW_FLAG_BKUP_REM)
+                       *cap++ = 'B';
+               if (flags & MPTCP_SUBFLOW_FLAG_BKUP_LOC)
+                       *cap++ = 'b';
+               if (flags & MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED)
+                       *cap++ = 'e';
+               if (flags & MPTCP_SUBFLOW_FLAG_CONNECTED)
+                       *cap++ = 'c';
+               if (flags & MPTCP_SUBFLOW_FLAG_MAPVALID)
+                       *cap++ = 'v';
+               if (flags)
+                       out(" flags:%s", caps);
+       }
+       if (tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM] &&
+           tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC] &&
+           tb[MPTCP_SUBFLOW_ATTR_ID_REM] &&
+           tb[MPTCP_SUBFLOW_ATTR_ID_LOC])
+               out(" token:%04x(id:%hhu)/%04x(id:%hhu)",
+                   rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM]),
+                   rta_getattr_u8(tb[MPTCP_SUBFLOW_ATTR_ID_REM]),
+                   rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC]),
+                   rta_getattr_u8(tb[MPTCP_SUBFLOW_ATTR_ID_LOC]));
+       if (tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ])
+               out(" seq:%llx",
+                   rta_getattr_u64(tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ]));
+       if (tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ])
+               out(" sfseq:%x",
+                   rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ]));
+       if (tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET])
+               out(" ssnoff:%x",
+                   rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET]));
+       if (tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN])
+               out(" maplen:%x",
+                   rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN]));
+}
+
 #define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt))
 
 static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
@@ -3021,6 +3106,14 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
                        tcp_tls_conf("rxconf", tlsinfo[TLS_INFO_RXCONF]);
                        tcp_tls_conf("txconf", tlsinfo[TLS_INFO_TXCONF]);
                }
+               if (ulpinfo[INET_ULP_INFO_MPTCP]) {
+                       struct rtattr *sfinfo[MPTCP_SUBFLOW_ATTR_MAX + 1] =
+                               { 0 };
+
+                       parse_rtattr_nested(sfinfo, MPTCP_SUBFLOW_ATTR_MAX,
+                                           ulpinfo[INET_ULP_INFO_MPTCP]);
+                       mptcp_subflow_info(sfinfo);
+               }
        }
 }
 
@@ -3104,6 +3197,9 @@ static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s)
        s->mark = 0;
        if (tb[INET_DIAG_MARK])
                s->mark = rta_getattr_u32(tb[INET_DIAG_MARK]);
+       s->cgroup_id = 0;
+       if (tb[INET_DIAG_CGROUP_ID])
+               s->cgroup_id = rta_getattr_u64(tb[INET_DIAG_CGROUP_ID]);
        if (tb[INET_DIAG_PROTOCOL])
                s->raw_prot = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]);
        else
@@ -3171,6 +3267,11 @@ static int inet_show_sock(struct nlmsghdr *nlh,
                        out(" class_id:%#x", rta_getattr_u32(tb[INET_DIAG_CLASS_ID]));
        }
 
+       if (show_cgroup) {
+               if (tb[INET_DIAG_CGROUP_ID])
+                       out(" cgroup:%s", cg_id_to_path(rta_getattr_u64(tb[INET_DIAG_CGROUP_ID])));
+       }
+
        if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) {
                if (!oneline)
                        out("\n\t");
@@ -4996,6 +5097,7 @@ static void _usage(FILE *dest)
 "       --tipcinfo      show internal tipc socket information\n"
 "   -s, --summary       show socket usage summary\n"
 "       --tos           show tos and priority information\n"
+"       --cgroup        show cgroup information\n"
 "   -b, --bpf           show bpf filter socket information\n"
 "   -E, --events        continually display sockets as they are destroyed\n"
 "   -Z, --context       display process SELinux security contexts\n"
@@ -5106,6 +5208,8 @@ static int scan_state(const char *state)
 /* Values of 'x' are already used so a non-character is used */
 #define OPT_XDPSOCK 260
 
+#define OPT_CGROUP 261
+
 static const struct option long_opts[] = {
        { "numeric", 0, 0, 'n' },
        { "resolve", 0, 0, 'r' },
@@ -5142,6 +5246,7 @@ static const struct option long_opts[] = {
        { "net", 1, 0, 'N' },
        { "tipcinfo", 0, 0, OPT_TIPCINFO},
        { "tos", 0, 0, OPT_TOS },
+       { "cgroup", 0, 0, OPT_CGROUP },
        { "kill", 0, 0, 'K' },
        { "no-header", 0, 0, 'H' },
        { "xdp", 0, 0, OPT_XDPSOCK},
@@ -5329,6 +5434,9 @@ int main(int argc, char *argv[])
                case OPT_TOS:
                        show_tos = 1;
                        break;
+               case OPT_CGROUP:
+                       show_cgroup = 1;
+                       break;
                case 'K':
                        current_filter.kill = 1;
                        break;
diff --git a/misc/ss_util.h b/misc/ss_util.h
new file mode 100644 (file)
index 0000000..f7e40bb
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __SS_UTIL_H__
+#define __SS_UTIL_H__
+
+#include <linux/sock_diag.h>
+#include <linux/inet_diag.h>
+
+#define MAGIC_SEQ 123456
+
+#define DIAG_REQUEST(_req, _r)                                             \
+       struct {                                                            \
+               struct nlmsghdr nlh;                                        \
+               _r;                                                         \
+       } _req = {                                                          \
+               .nlh = {                                                    \
+                       .nlmsg_type = SOCK_DIAG_BY_FAMILY,                  \
+                       .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\
+                       .nlmsg_seq = MAGIC_SEQ,                             \
+                       .nlmsg_len = sizeof(_req),                          \
+               },                                                          \
+       }
+
+#endif /* __SS_UTIL_H__ */
index f5b0bc8a16e72961cd925e971bd164f245a60e6b..0be3b1e03b9f7c4b184ae8cf4ab4d906bbbc9770 100644 (file)
@@ -1,19 +1,24 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#define SSF_DCOND 0
-#define SSF_SCOND 1
-#define SSF_OR   2
-#define SSF_AND          3
-#define SSF_NOT          4
-#define SSF_D_GE  5
-#define SSF_D_LE  6
-#define SSF_S_GE  7
-#define SSF_S_LE  8
-#define SSF_S_AUTO  9
-#define SSF_DEVCOND 10
-#define SSF_MARKMASK 11
-
 #include <stdbool.h>
 
+enum {
+       SSF_DCOND,
+       SSF_SCOND,
+       SSF_OR,
+       SSF_AND,
+       SSF_NOT,
+       SSF_D_GE,
+       SSF_D_LE,
+       SSF_S_GE,
+       SSF_S_LE,
+       SSF_S_AUTO,
+       SSF_DEVCOND,
+       SSF_MARKMASK,
+       SSF_CGROUPCOND,
+       SSF__MAX
+};
+
+bool ssfilter_is_supported(int type);
+
 struct ssfilter
 {
        int type;
@@ -25,3 +30,4 @@ int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp);
 void *parse_hostcond(char *addr, bool is_port);
 void *parse_devcond(char *name);
 void *parse_markmask(const char *markmask);
+void *parse_cgroupcond(const char *path);
index a901ae753a284794205c0c0907ae3cad0aa65cc1..8e16b44638f6078e237fb3796d864708046b7072 100644 (file)
@@ -12,7 +12,14 @@ typedef struct ssfilter * ssfilter_t;
 
 static struct ssfilter * alloc_node(int type, void *pred)
 {
-       struct ssfilter *n = malloc(sizeof(*n));
+       struct ssfilter *n;
+
+       if (!ssfilter_is_supported(type)) {
+               fprintf(stderr, "It looks like such filter is not supported! Too old kernel?\n");
+               exit(-1);
+       }
+
+       n = malloc(sizeof(*n));
        if (n == NULL)
                abort();
        n->type = type;
@@ -36,7 +43,7 @@ static void yyerror(char *s)
 
 %}
 
-%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK
+%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK CGROUPCOND CGROUPPATH
 %left '|'
 %left '&'
 %nonassoc '!'
@@ -156,6 +163,14 @@ expr:      '(' exprlist ')'
         {
                 $$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3));
         }
+        | CGROUPPATH eq CGROUPCOND
+        {
+                $$ = alloc_node(SSF_CGROUPCOND, $3);
+        }
+        | CGROUPPATH NEQ CGROUPCOND
+        {
+                $$ = alloc_node(SSF_NOT, alloc_node(SSF_CGROUPCOND, $3));
+        }
         | AUTOBOUND
         {
                 $$ = alloc_node(SSF_S_AUTO, NULL);
@@ -276,6 +291,10 @@ int yylex(void)
                tok_type = FWMARK;
                return FWMARK;
        }
+       if (strcmp(curtok, "cgroup") == 0) {
+               tok_type = CGROUPPATH;
+               return CGROUPPATH;
+       }
        if (strcmp(curtok, ">=") == 0 ||
            strcmp(curtok, "ge") == 0 ||
            strcmp(curtok, "geq") == 0)
@@ -318,6 +337,14 @@ int yylex(void)
                }
                return MARKMASK;
        }
+       if (tok_type == CGROUPPATH) {
+               yylval = (void*)parse_cgroupcond(curtok);
+               if (yylval == NULL) {
+                       fprintf(stderr, "Cannot parse cgroup %s.\n", curtok);
+                       exit(1);
+               }
+               return CGROUPCOND;
+       }
        yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT);
        if (yylval == NULL) {
                fprintf(stderr, "Cannot parse dst/src address.\n");
diff --git a/misc/ssfilter_check.c b/misc/ssfilter_check.c
new file mode 100644 (file)
index 0000000..38c960c
--- /dev/null
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "libnetlink.h"
+#include "ssfilter.h"
+#include "ss_util.h"
+
+static int dummy_filter(struct nlmsghdr *n, void *arg)
+{
+       /* just stops rtnl_dump_filter() */
+       return -1;
+}
+
+static bool cgroup_filter_check(void)
+{
+       struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+       DIAG_REQUEST(req, struct inet_diag_req_v2 r);
+       struct instr {
+               struct inet_diag_bc_op op;
+               __u64 cgroup_id;
+       } __attribute__((packed));
+       int inslen = sizeof(struct instr);
+       struct instr instr = {
+               { INET_DIAG_BC_CGROUP_COND, inslen, inslen + 4 },
+               0
+       };
+       struct rtnl_handle rth;
+       struct iovec iov[3];
+       struct msghdr msg;
+       struct rtattr rta;
+       int ret = false;
+       int iovlen = 3;
+
+       if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG))
+               return false;
+       rth.dump = MAGIC_SEQ;
+       rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR;
+
+       memset(&req.r, 0, sizeof(req.r));
+       req.r.sdiag_family = AF_INET;
+       req.r.sdiag_protocol = IPPROTO_TCP;
+       req.nlh.nlmsg_len += RTA_LENGTH(inslen);
+
+       rta.rta_type = INET_DIAG_REQ_BYTECODE;
+       rta.rta_len = RTA_LENGTH(inslen);
+
+       iov[0] = (struct iovec) { &req, sizeof(req) };
+       iov[1] = (struct iovec) { &rta, sizeof(rta) };
+       iov[2] = (struct iovec) { &instr, inslen };
+
+       msg = (struct msghdr) {
+               .msg_name = (void *)&nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = iov,
+               .msg_iovlen = iovlen,
+       };
+
+       if (sendmsg(rth.fd, &msg, 0) < 0)
+               goto out;
+
+       if (rtnl_dump_filter(&rth, dummy_filter, NULL) < 0) {
+               ret = (errno != EINVAL);
+               goto out;
+       }
+
+       ret = true;
+
+out:
+       rtnl_close(&rth);
+
+       return ret;
+}
+
+
+struct filter_check_t {
+       bool (*check)(void);
+       int checked:1,
+           supported:1;
+};
+
+static struct filter_check_t filter_checks[SSF__MAX] = {
+       [SSF_CGROUPCOND] = { cgroup_filter_check, 0 },
+};
+
+bool ssfilter_is_supported(int type)
+{
+       struct filter_check_t f;
+
+       if (type >= SSF__MAX)
+               return false;
+
+       f = filter_checks[type];
+       if (!f.check)
+               return true;
+
+       if (!f.checked) {
+               f.supported = f.check();
+               f.checked = 1;
+       }
+
+       return f.supported;
+}
index e31cbc12ec423c78357c619f68154f77d7e6f994..79c9c1dd9a0ecdd3ff8530ac28f466b24d98a9fa 100644 (file)
@@ -54,6 +54,7 @@ TCMODULES += m_bpf.o
 TCMODULES += m_tunnel_key.o
 TCMODULES += m_sample.o
 TCMODULES += m_ct.o
+TCMODULES += m_gate.o
 TCMODULES += p_ip.o
 TCMODULES += p_ip6.o
 TCMODULES += p_icmp.o
index 135271aa16978c34d41e19b6e8ea3b261f9b07d2..fa3552aefffdca88553b479432b3b9d638582640 100644 (file)
@@ -203,22 +203,24 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
        parse_rtattr_nested(tb, TCA_BPF_MAX, opt);
 
        if (handle)
-               fprintf(f, "handle 0x%x ", handle);
+               print_0xhex(PRINT_ANY, "handle", "handle %#llx ", handle);
 
        if (tb[TCA_BPF_CLASSID]) {
                SPRINT_BUF(b1);
-               fprintf(f, "flowid %s ",
+               print_string(PRINT_ANY, "flowid", "flowid %s ",
                        sprint_tc_classid(rta_getattr_u32(tb[TCA_BPF_CLASSID]), b1));
        }
 
        if (tb[TCA_BPF_NAME])
-               fprintf(f, "%s ", rta_getattr_str(tb[TCA_BPF_NAME]));
+               print_string(PRINT_ANY, "bpf_name", "%s ",
+                            rta_getattr_str(tb[TCA_BPF_NAME]));
 
        if (tb[TCA_BPF_FLAGS]) {
                unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]);
 
                if (flags & TCA_BPF_FLAG_ACT_DIRECT)
-                       fprintf(f, "direct-action ");
+                       print_bool(PRINT_ANY,
+                                  "direct-action", "direct-action ", true);
        }
 
        if (tb[TCA_BPF_FLAGS_GEN]) {
@@ -226,14 +228,14 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
                        rta_getattr_u32(tb[TCA_BPF_FLAGS_GEN]);
 
                if (flags & TCA_CLS_FLAGS_SKIP_HW)
-                       fprintf(f, "skip_hw ");
+                       print_bool(PRINT_ANY, "skip_hw", "skip_hw ", true);
                if (flags & TCA_CLS_FLAGS_SKIP_SW)
-                       fprintf(f, "skip_sw ");
-
+                       print_bool(PRINT_ANY, "skip_sw", "skip_sw ", true);
                if (flags & TCA_CLS_FLAGS_IN_HW)
-                       fprintf(f, "in_hw ");
+                       print_bool(PRINT_ANY, "in_hw", "in_hw ", true);
                else if (flags & TCA_CLS_FLAGS_NOT_IN_HW)
-                       fprintf(f, "not_in_hw ");
+                       print_bool(PRINT_ANY,
+                                  "not_in_hw", "not_in_hw ", true);
        }
 
        if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN])
@@ -245,14 +247,13 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
        if (!dump_ok && tb[TCA_BPF_TAG]) {
                SPRINT_BUF(b);
 
-               fprintf(f, "tag %s ",
-                       hexstring_n2a(RTA_DATA(tb[TCA_BPF_TAG]),
-                                     RTA_PAYLOAD(tb[TCA_BPF_TAG]),
-                                     b, sizeof(b)));
+               print_string(PRINT_ANY, "tag", "tag %s ",
+                            hexstring_n2a(RTA_DATA(tb[TCA_BPF_TAG]),
+                            RTA_PAYLOAD(tb[TCA_BPF_TAG]), b, sizeof(b)));
        }
 
        if (tb[TCA_BPF_POLICE]) {
-               fprintf(f, "\n");
+               print_nl();
                tc_print_police(f, tb[TCA_BPF_POLICE]);
        }
 
index 9d59d71f9cd6292821a46fad32571825d1ae5633..fc13691152e5fe531220737f3928a6eb3b14febf 100644 (file)
@@ -81,6 +81,8 @@ static void explain(void)
                "                       enc_tos MASKED-IP_TOS |\n"
                "                       enc_ttl MASKED-IP_TTL |\n"
                "                       geneve_opts MASKED-OPTIONS |\n"
+               "                       vxlan_opts MASKED-OPTIONS |\n"
+               "                       erspan_opts MASKED-OPTIONS |\n"
                "                       ip_flags IP-FLAGS | \n"
                "                       enc_dst_port [ port_number ] |\n"
                "                       ct_state MASKED_CT_STATE |\n"
@@ -847,7 +849,7 @@ static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n)
        return 0;
 }
 
-static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
+static int flower_parse_geneve_opt(char *str, struct nlmsghdr *n)
 {
        struct rtattr *nest;
        char *token;
@@ -917,14 +919,111 @@ static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
        return 0;
 }
 
-static int flower_parse_enc_opt_part(char *str, struct nlmsghdr *n)
+static int flower_parse_vxlan_opt(char *str, struct nlmsghdr *n)
+{
+       struct rtattr *nest;
+       __u32 gbp;
+       int err;
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_VXLAN | NLA_F_NESTED);
+
+       err = get_u32(&gbp, str, 0);
+       if (err)
+               return err;
+       addattr32(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp);
+
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
+static int flower_parse_erspan_opt(char *str, struct nlmsghdr *n)
+{
+       struct rtattr *nest;
+       char *token;
+       int i, err;
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_ERSPAN | NLA_F_NESTED);
+
+       i = 1;
+       token = strsep(&str, ":");
+       while (token) {
+               switch (i) {
+               case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       addattr8(n, MAX_MSG, i, opt_type);
+                       break;
+               }
+               case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX:
+               {
+                       __be32 opt_index;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_be32(&opt_index, token, 0);
+                       if (err)
+                               return err;
+
+                       addattr32(n, MAX_MSG, i, opt_index);
+                       break;
+               }
+               case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       addattr8(n, MAX_MSG, i, opt_type);
+                       break;
+               }
+               case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID:
+               {
+                       __u8 opt_type;
+
+                       if (!strlen(token))
+                               break;
+                       err = get_u8(&opt_type, token, 0);
+                       if (err)
+                               return err;
+
+                       addattr8(n, MAX_MSG, i, opt_type);
+                       break;
+               }
+               default:
+                       fprintf(stderr, "Unknown \"geneve_opts\" type\n");
+                       return -1;
+               }
+
+               token = strsep(&str, ":");
+               i++;
+       }
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
+static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
 {
        char *token;
        int err;
 
        token = strsep(&str, ",");
        while (token) {
-               err = flower_parse_geneve_opts(token, n);
+               err = flower_parse_geneve_opt(token, n);
                if (err)
                        return err;
 
@@ -954,7 +1053,7 @@ static int flower_check_enc_opt_key(char *key)
        return 0;
 }
 
-static int flower_parse_enc_opts(char *str, struct nlmsghdr *n)
+static int flower_parse_enc_opts_geneve(char *str, struct nlmsghdr *n)
 {
        char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
        int data_len, key_len, mask_len, err;
@@ -1006,13 +1105,93 @@ static int flower_parse_enc_opts(char *str, struct nlmsghdr *n)
        mask[mask_len - 1] = '\0';
 
        nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS);
-       err = flower_parse_enc_opt_part(key, n);
+       err = flower_parse_geneve_opts(key, n);
        if (err)
                return err;
        addattr_nest_end(n, nest);
 
        nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK);
-       err = flower_parse_enc_opt_part(mask, n);
+       err = flower_parse_geneve_opts(mask, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
+static int flower_parse_enc_opts_vxlan(char *str, struct nlmsghdr *n)
+{
+       char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
+       struct rtattr *nest;
+       char *slash;
+       int err;
+
+       slash = strchr(str, '/');
+       if (slash) {
+               *slash++ = '\0';
+               if (strlen(slash) > XATTR_SIZE_MAX)
+                       return -1;
+               strcpy(mask, slash);
+       } else {
+               strcpy(mask, "0xffffffff");
+       }
+
+       if (strlen(str) > XATTR_SIZE_MAX)
+               return -1;
+       strcpy(key, str);
+
+       nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
+       err = flower_parse_vxlan_opt(str, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
+       err = flower_parse_vxlan_opt(mask, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       return 0;
+}
+
+static int flower_parse_enc_opts_erspan(char *str, struct nlmsghdr *n)
+{
+       char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
+       struct rtattr *nest;
+       char *slash;
+       int err;
+
+
+       slash = strchr(str, '/');
+       if (slash) {
+               *slash++ = '\0';
+               if (strlen(slash) > XATTR_SIZE_MAX)
+                       return -1;
+               strcpy(mask, slash);
+       } else {
+               int index;
+
+               slash = strchr(str, ':');
+               index = (int)(slash - str);
+               memcpy(mask, str, index);
+               strcpy(mask + index, ":0xffffffff:0xff:0xff");
+       }
+
+       if (strlen(str) > XATTR_SIZE_MAX)
+               return -1;
+       strcpy(key, str);
+
+       nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
+       err = flower_parse_erspan_opt(key, n);
+       if (err)
+               return err;
+       addattr_nest_end(n, nest);
+
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
+       err = flower_parse_erspan_opt(mask, n);
        if (err)
                return err;
        addattr_nest_end(n, nest);
@@ -1502,11 +1681,25 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
                        }
                } else if (matches(*argv, "geneve_opts") == 0) {
                        NEXT_ARG();
-                       ret = flower_parse_enc_opts(*argv, n);
+                       ret = flower_parse_enc_opts_geneve(*argv, n);
                        if (ret < 0) {
                                fprintf(stderr, "Illegal \"geneve_opts\"\n");
                                return -1;
                        }
+               } else if (matches(*argv, "vxlan_opts") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_enc_opts_vxlan(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"vxlan_opts\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "erspan_opts") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_enc_opts_erspan(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"erspan_opts\"\n");
+                               return -1;
+                       }
                } else if (matches(*argv, "action") == 0) {
                        NEXT_ARG();
                        ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
@@ -1940,10 +2133,61 @@ static void flower_print_geneve_opts(const char *name, struct rtattr *attr,
        close_json_array(PRINT_JSON, name);
 }
 
-static void flower_print_geneve_parts(const char *name, struct rtattr *attr,
-                                     char *key, char *mask)
+static void flower_print_vxlan_opts(const char *name, struct rtattr *attr,
+                                   char *strbuf)
+{
+       struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       __u32 gbp;
+
+       parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, i, rem);
+       gbp = rta_getattr_u32(tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]);
+
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_JSON, "gbp", NULL, gbp);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+
+       sprintf(strbuf, "%u", gbp);
+}
+
+static void flower_print_erspan_opts(const char *name, struct rtattr *attr,
+                                    char *strbuf)
+{
+       struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1];
+       __u8 ver, hwid, dir;
+       __u32 idx;
+
+       parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX, RTA_DATA(attr),
+                    RTA_PAYLOAD(attr));
+       ver = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER]);
+       if (ver == 1) {
+               idx = rta_getattr_be32(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX]);
+               hwid = 0;
+               dir = 0;
+       } else {
+               idx = 0;
+               hwid = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID]);
+               dir = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR]);
+       }
+
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_JSON, "ver", NULL, ver);
+       print_uint(PRINT_JSON, "index", NULL, idx);
+       print_uint(PRINT_JSON, "dir", NULL, dir);
+       print_uint(PRINT_JSON, "hwid", NULL, hwid);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+
+       sprintf(strbuf, "%u:%u:%u:%u", ver, idx, dir, hwid);
+}
+
+static void flower_print_enc_parts(const char *name, const char *namefrm,
+                                  struct rtattr *attr, char *key, char *mask)
 {
-       char *namefrm = "  geneve_opt %s";
        char *key_token, *mask_token, *out;
        int len;
 
@@ -1985,14 +2229,39 @@ static void flower_print_enc_opts(const char *name, struct rtattr *attr,
                goto err_key_free;
 
        parse_rtattr_nested(key_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, attr);
-       flower_print_geneve_opts("geneve_opt_key",
-                                key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key);
-
        parse_rtattr_nested(msk_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, mask_attr);
-       flower_print_geneve_opts("geneve_opt_mask",
-                                msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk);
 
-       flower_print_geneve_parts(name, attr, key, msk);
+       if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE]) {
+               flower_print_geneve_opts("geneve_opt_key",
+                               key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key);
+
+               if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE])
+                       flower_print_geneve_opts("geneve_opt_mask",
+                               msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk);
+
+               flower_print_enc_parts(name, "  geneve_opts %s", attr, key,
+                                      msk);
+       } else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN]) {
+               flower_print_vxlan_opts("vxlan_opt_key",
+                               key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], key);
+
+               if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN])
+                       flower_print_vxlan_opts("vxlan_opt_mask",
+                               msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], msk);
+
+               flower_print_enc_parts(name, "  vxlan_opts %s", attr, key,
+                                      msk);
+       } else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN]) {
+               flower_print_erspan_opts("erspan_opt_key",
+                               key_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN], key);
+
+               if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN])
+                       flower_print_erspan_opts("erspan_opt_mask",
+                               msk_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN], msk);
+
+               flower_print_enc_parts(name, "  erspan_opts %s", attr, key,
+                                      msk);
+       }
 
        free(msk);
 err_key_free:
diff --git a/tc/m_gate.c b/tc/m_gate.c
new file mode 100644 (file)
index 0000000..327df7e
--- /dev/null
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Copyright 2020 NXP */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/if_ether.h>
+#include "utils.h"
+#include "rt_names.h"
+#include "tc_util.h"
+#include "list.h"
+#include <linux/tc_act/tc_gate.h>
+
+struct gate_entry {
+       struct list_head list;
+       uint8_t gate_state;
+       uint32_t interval;
+       int32_t ipv;
+       int32_t maxoctets;
+};
+
+#define CLOCKID_INVALID (-1)
+static const struct clockid_table {
+       const char *name;
+       clockid_t clockid;
+} clockt_map[] = {
+       { "REALTIME", CLOCK_REALTIME },
+       { "TAI", CLOCK_TAI },
+       { "BOOTTIME", CLOCK_BOOTTIME },
+       { "MONOTONIC", CLOCK_MONOTONIC },
+       { NULL }
+};
+
+static void explain(void)
+{
+       fprintf(stderr,
+               "Usage: gate [ priority PRIO-SPEC ] [ base-time BASE-TIME ]\n"
+               "       [ cycle-time CYCLE-TIME ]\n"
+               "       [ cycle-time-ext CYCLE-TIME-EXT ]\n"
+               "       [ clockid CLOCKID ] [flags FLAGS]\n"
+               "       [ sched-entry GATE0 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
+               "       [ sched-entry GATE1 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
+               "       ......\n"
+               "       [ sched-entry GATEn INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
+               "       [ CONTROL ]\n"
+               "       GATEn := open | close\n"
+               "       INTERVAL : nanoseconds period of gate slot\n"
+               "       INTERNAL-PRIO-VALUE : internal priority decide which\n"
+               "                             rx queue number direct to.\n"
+               "                             default to be -1 which means wildcard.\n"
+               "       MAX-OCTETS : maximum number of MSDU octets that are\n"
+               "                    permitted to pas the gate during the\n"
+               "                    specified TimeInterval.\n"
+               "                    default to be -1 which means wildcard.\n"
+               "       CONTROL := pipe | drop | continue | pass |\n"
+               "                  goto chain <CHAIN_INDEX>\n");
+}
+
+static void usage(void)
+{
+       explain();
+       exit(-1);
+}
+
+static void explain_entry_format(void)
+{
+       fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n");
+}
+
+static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
+                     int tca_id, struct nlmsghdr *n);
+static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg);
+
+struct action_util gate_action_util = {
+       .id = "gate",
+       .parse_aopt = parse_gate,
+       .print_aopt = print_gate,
+};
+
+static int get_clockid(__s32 *val, const char *arg)
+{
+       const struct clockid_table *c;
+
+       if (strcasestr(arg, "CLOCK_") != NULL)
+               arg += sizeof("CLOCK_") - 1;
+
+       for (c = clockt_map; c->name; c++) {
+               if (strcasecmp(c->name, arg) == 0) {
+                       *val = c->clockid;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static const char *get_clock_name(clockid_t clockid)
+{
+       const struct clockid_table *c;
+
+       for (c = clockt_map; c->name; c++) {
+               if (clockid == c->clockid)
+                       return c->name;
+       }
+
+       return "invalid";
+}
+
+static int get_gate_state(__u8 *val, const char *arg)
+{
+       if (!strcasecmp("OPEN", arg)) {
+               *val = 1;
+               return 0;
+       }
+
+       if (!strcasecmp("CLOSE", arg)) {
+               *val = 0;
+               return 0;
+       }
+
+       return -1;
+}
+
+static struct gate_entry *create_gate_entry(uint8_t gate_state,
+                                           uint32_t interval,
+                                           int32_t ipv,
+                                           int32_t maxoctets)
+{
+       struct gate_entry *e;
+
+       e = calloc(1, sizeof(*e));
+       if (!e)
+               return NULL;
+
+       e->gate_state = gate_state;
+       e->interval = interval;
+       e->ipv = ipv;
+       e->maxoctets = maxoctets;
+
+       return e;
+}
+
+static int add_gate_list(struct list_head *gate_entries, struct nlmsghdr *n)
+{
+       struct gate_entry *e;
+
+       list_for_each_entry(e, gate_entries, list) {
+               struct rtattr *a;
+
+               a = addattr_nest(n, 1024, TCA_GATE_ONE_ENTRY | NLA_F_NESTED);
+
+               if (e->gate_state)
+                       addattr(n, MAX_MSG, TCA_GATE_ENTRY_GATE);
+
+               addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_INTERVAL,
+                         &e->interval, sizeof(e->interval));
+               addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_IPV,
+                         &e->ipv, sizeof(e->ipv));
+               addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_MAX_OCTETS,
+                         &e->maxoctets, sizeof(e->maxoctets));
+
+               addattr_nest_end(n, a);
+       }
+
+       return 0;
+}
+
+static void free_entries(struct list_head *gate_entries)
+{
+       struct gate_entry *e, *n;
+
+       list_for_each_entry_safe(e, n, gate_entries, list) {
+               list_del(&e->list);
+               free(e);
+       }
+}
+
+static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
+                     int tca_id, struct nlmsghdr *n)
+{
+       struct tc_gate parm = {.action = TC_ACT_PIPE};
+       struct list_head gate_entries;
+       __s32 clockid = CLOCKID_INVALID;
+       struct rtattr *tail, *nle;
+       char **argv = *argv_p;
+       int argc = *argc_p;
+       __s64 base_time = 0;
+       __s64 cycle_time = 0;
+       __s64 cycle_time_ext = 0;
+       int entry_num = 0;
+       char *invalidarg;
+       __u32 flags = 0;
+       int prio = -1;
+
+       int err;
+
+       if (matches(*argv, "gate") != 0)
+               return -1;
+
+       NEXT_ARG();
+       if (argc <= 0)
+               return -1;
+
+       INIT_LIST_HEAD(&gate_entries);
+
+       while (argc > 0) {
+               if (matches(*argv, "index") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&parm.index, *argv, 10)) {
+                               invalidarg = "index";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "priority") == 0) {
+                       NEXT_ARG();
+                       if (get_s32(&prio, *argv, 0)) {
+                               invalidarg = "priority";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "base-time") == 0) {
+                       NEXT_ARG();
+                       if (get_s64(&base_time, *argv, 10) &&
+                           get_time64(&base_time, *argv)) {
+                               invalidarg = "base-time";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "cycle-time") == 0) {
+                       NEXT_ARG();
+                       if (get_s64(&cycle_time, *argv, 10) &&
+                           get_time64(&cycle_time, *argv)) {
+                               invalidarg = "cycle-time";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "cycle-time-ext") == 0) {
+                       NEXT_ARG();
+                       if (get_s64(&cycle_time_ext, *argv, 10) &&
+                           get_time64(&cycle_time_ext, *argv)) {
+                               invalidarg = "cycle-time-ext";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "clockid") == 0) {
+                       NEXT_ARG();
+                       if (get_clockid(&clockid, *argv)) {
+                               invalidarg = "clockid";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "flags") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&flags, *argv, 0)) {
+                               invalidarg = "flags";
+                               goto err_arg;
+                       }
+               } else if (matches(*argv, "sched-entry") == 0) {
+                       unsigned int maxoctets_uint = 0;
+                       int32_t maxoctets = -1;
+                       struct gate_entry *e;
+                       uint8_t gate_state = 0;
+                       __s64 interval_s64 = 0;
+                       uint32_t interval = 0;
+                       int32_t ipv = -1;
+
+                       if (!NEXT_ARG_OK()) {
+                               explain_entry_format();
+                               fprintf(stderr, "\"sched-entry\" is imcomplete\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       }
+
+                       NEXT_ARG();
+
+                       if (get_gate_state(&gate_state, *argv)) {
+                               explain_entry_format();
+                               fprintf(stderr, "\"sched-entry\" is imcomplete\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       }
+
+                       if (!NEXT_ARG_OK()) {
+                               explain_entry_format();
+                               fprintf(stderr, "\"sched-entry\" is imcomplete\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       }
+
+                       NEXT_ARG();
+
+                       if (get_u32(&interval, *argv, 0) &&
+                           get_time64(&interval_s64, *argv)) {
+                               explain_entry_format();
+                               fprintf(stderr, "\"sched-entry\" is imcomplete\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       }
+
+                       if (interval_s64 > UINT_MAX) {
+                               fprintf(stderr, "\"interval\" is too large\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       } else if (interval_s64) {
+                               interval = interval_s64;
+                       }
+
+                       if (!NEXT_ARG_OK())
+                               goto create_entry;
+
+                       NEXT_ARG();
+
+                       if (get_s32(&ipv, *argv, 0)) {
+                               PREV_ARG();
+                               goto create_entry;
+                       }
+
+                       if (!gate_state)
+                               ipv = -1;
+
+                       if (!NEXT_ARG_OK())
+                               goto create_entry;
+
+                       NEXT_ARG();
+
+                       if (get_s32(&maxoctets, *argv, 0) &&
+                           get_size(&maxoctets_uint, *argv))
+                               PREV_ARG();
+
+                       if (maxoctets_uint > INT_MAX) {
+                               fprintf(stderr, "\"maxoctets\" is too large\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       } else if (maxoctets_uint ) {
+                               maxoctets = maxoctets_uint;
+                       }
+
+                       if (!gate_state)
+                               maxoctets = -1;
+
+create_entry:
+                       e = create_gate_entry(gate_state, interval,
+                                             ipv, maxoctets);
+                       if (!e) {
+                               fprintf(stderr, "gate: not enough memory\n");
+                               free_entries(&gate_entries);
+                               return -1;
+                       }
+
+                       list_add_tail(&e->list, &gate_entries);
+                       entry_num++;
+               } else if (matches(*argv, "help") == 0) {
+                       usage();
+               } else {
+                       break;
+               }
+
+               argc--;
+               argv++;
+       }
+
+       parse_action_control_dflt(&argc, &argv, &parm.action,
+                                 false, TC_ACT_PIPE);
+
+       if (!entry_num && !parm.index) {
+               fprintf(stderr, "gate: must add at least one entry\n");
+               return -1;
+       }
+
+       tail = addattr_nest(n, MAX_MSG, tca_id | NLA_F_NESTED);
+       addattr_l(n, MAX_MSG, TCA_GATE_PARMS, &parm, sizeof(parm));
+
+       if (prio != -1)
+               addattr_l(n, MAX_MSG, TCA_GATE_PRIORITY, &prio, sizeof(prio));
+
+       if (flags)
+               addattr_l(n, MAX_MSG, TCA_GATE_FLAGS, &flags, sizeof(flags));
+
+       if (base_time)
+               addattr_l(n, MAX_MSG, TCA_GATE_BASE_TIME,
+                         &base_time, sizeof(base_time));
+
+       if (cycle_time)
+               addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME,
+                         &cycle_time, sizeof(cycle_time));
+
+       if (cycle_time_ext)
+               addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME_EXT,
+                         &cycle_time_ext, sizeof(cycle_time_ext));
+
+       if (clockid != CLOCKID_INVALID)
+               addattr_l(n, MAX_MSG, TCA_GATE_CLOCKID,
+                         &clockid, sizeof(clockid));
+
+       nle = addattr_nest(n, MAX_MSG, TCA_GATE_ENTRY_LIST | NLA_F_NESTED);
+       err = add_gate_list(&gate_entries, n);
+       if (err < 0) {
+               fprintf(stderr, "Could not add entries to netlink message\n");
+               free_entries(&gate_entries);
+               return -1;
+       }
+
+       addattr_nest_end(n, nle);
+       addattr_nest_end(n, tail);
+       free_entries(&gate_entries);
+       *argc_p = argc;
+       *argv_p = argv;
+
+       return 0;
+err_arg:
+       invarg(invalidarg, *argv);
+       free_entries(&gate_entries);
+
+       return -1;
+}
+
+static int print_gate_list(struct rtattr *list)
+{
+       struct rtattr *item;
+       int rem;
+
+       rem = RTA_PAYLOAD(list);
+
+       print_string(PRINT_FP, NULL, "%s", _SL_);
+       print_string(PRINT_FP, NULL, "\tschedule:%s", _SL_);
+       open_json_array(PRINT_JSON, "schedule");
+
+       for (item = RTA_DATA(list);
+            RTA_OK(item, rem);
+            item = RTA_NEXT(item, rem)) {
+               struct rtattr *tb[TCA_GATE_ENTRY_MAX + 1];
+               __u32 index = 0, interval = 0;
+               __u8 gate_state = 0;
+               __s32 ipv = -1, maxoctets = -1;
+               char buf[22];
+
+               parse_rtattr_nested(tb, TCA_GATE_ENTRY_MAX, item);
+
+               if (tb[TCA_GATE_ENTRY_INDEX])
+                       index = rta_getattr_u32(tb[TCA_GATE_ENTRY_INDEX]);
+
+               if (tb[TCA_GATE_ENTRY_GATE])
+                       gate_state = 1;
+
+               if (tb[TCA_GATE_ENTRY_INTERVAL])
+                       interval = rta_getattr_u32(tb[TCA_GATE_ENTRY_INTERVAL]);
+
+               if (tb[TCA_GATE_ENTRY_IPV])
+                       ipv = rta_getattr_s32(tb[TCA_GATE_ENTRY_IPV]);
+
+               if (tb[TCA_GATE_ENTRY_MAX_OCTETS])
+                       maxoctets = rta_getattr_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]);
+
+               open_json_object(NULL);
+               print_uint(PRINT_ANY, "number", "\t number %4u", index);
+               print_string(PRINT_ANY, "gate_state", "\tgate-state %s ",
+                            gate_state ? "open" : "close");
+
+               print_uint(PRINT_JSON, "interval", NULL, interval);
+
+               memset(buf, 0, sizeof(buf));
+               print_string(PRINT_FP, NULL, "\tinterval %s",
+                            sprint_time64(interval, buf));
+
+               if (ipv != -1) {
+                       print_uint(PRINT_ANY, "ipv", "\t ipv %-10u", ipv);
+               } else {
+                       print_int(PRINT_JSON, "ipv", NULL, ipv);
+                       print_string(PRINT_FP, NULL, "\t ipv %s", "wildcard");
+               }
+
+               if (maxoctets != -1) {
+                       memset(buf, 0, sizeof(buf));
+                       print_uint(PRINT_JSON, "max_octets", NULL, maxoctets);
+                       print_string(PRINT_FP, NULL, "\t max-octets %s",
+                                    sprint_size(maxoctets, buf));
+               } else {
+                       print_string(PRINT_FP, NULL,
+                                    "\t max-octets %s", "wildcard");
+                       print_int(PRINT_JSON, "max_octets", NULL, maxoctets);
+               }
+
+               close_json_object();
+               print_string(PRINT_FP, NULL, "%s", _SL_);
+       }
+
+       close_json_array(PRINT_ANY, "");
+
+       return 0;
+}
+
+static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg)
+{
+       struct tc_gate *parm;
+       struct rtattr *tb[TCA_GATE_MAX + 1];
+       __s32 clockid = CLOCKID_INVALID;
+       __s64 base_time = 0;
+       __s64 cycle_time = 0;
+       __s64 cycle_time_ext = 0;
+       char buf[22];
+       int prio = -1;
+
+       if (arg == NULL)
+               return -1;
+
+       parse_rtattr_nested(tb, TCA_GATE_MAX, arg);
+
+       if (!tb[TCA_GATE_PARMS]) {
+               fprintf(stderr, "Missing gate parameters\n");
+               return -1;
+       }
+
+       print_string(PRINT_FP, NULL, "%s", "\n");
+
+       parm = RTA_DATA(tb[TCA_GATE_PARMS]);
+
+       if (tb[TCA_GATE_PRIORITY])
+               prio = rta_getattr_s32(tb[TCA_GATE_PRIORITY]);
+
+       if (prio != -1) {
+               print_int(PRINT_ANY, "priority", "\tpriority %-8d", prio);
+       } else {
+               print_string(PRINT_FP, NULL, "\tpriority %s", "wildcard");
+               print_int(PRINT_JSON, "priority", NULL, prio);
+       }
+
+       if (tb[TCA_GATE_CLOCKID])
+               clockid = rta_getattr_s32(tb[TCA_GATE_CLOCKID]);
+       print_string(PRINT_ANY, "clockid", "\tclockid %s",
+                    get_clock_name(clockid));
+
+       if (tb[TCA_GATE_FLAGS]) {
+               __u32 flags;
+
+               flags = rta_getattr_u32(tb[TCA_GATE_FLAGS]);
+               print_0xhex(PRINT_ANY, "flags", "\tflags %#x", flags);
+       }
+
+       print_string(PRINT_FP, NULL, "%s", "\n");
+
+       if (tb[TCA_GATE_BASE_TIME])
+               base_time = rta_getattr_s64(tb[TCA_GATE_BASE_TIME]);
+
+       memset(buf, 0, sizeof(buf));
+       print_string(PRINT_FP, NULL, "\tbase-time %s",
+                    sprint_time64(base_time, buf));
+       print_lluint(PRINT_JSON, "base_time", NULL, base_time);
+
+       if (tb[TCA_GATE_CYCLE_TIME])
+               cycle_time = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME]);
+
+       memset(buf, 0, sizeof(buf));
+       print_string(PRINT_FP, NULL,
+                    "\tcycle-time %s", sprint_time64(cycle_time, buf));
+       print_lluint(PRINT_JSON, "cycle_time", NULL, cycle_time);
+
+       if (tb[TCA_GATE_CYCLE_TIME_EXT])
+               cycle_time_ext = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME_EXT]);
+
+       memset(buf, 0, sizeof(buf));
+       print_string(PRINT_FP, NULL, "\tcycle-time-ext %s",
+                    sprint_time64(cycle_time_ext, buf));
+       print_lluint(PRINT_JSON, "cycle_time_ext", NULL, cycle_time_ext);
+
+       if (tb[TCA_GATE_ENTRY_LIST])
+               print_gate_list(tb[TCA_GATE_ENTRY_LIST]);
+
+       print_action_control(f, "\t", parm->action, "");
+
+       print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index);
+       print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
+       print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
+
+       if (show_stats) {
+               if (tb[TCA_GATE_TM]) {
+                       struct tcf_t *tm = RTA_DATA(tb[TCA_GATE_TM]);
+
+                       print_tm(f, tm);
+               }
+       }
+
+       print_string(PRINT_FP, NULL, "%s", "\n");
+
+       return 0;
+}
index fccfd17ca2709ef169e765f476b1374bd0d631a6..51dcf10930e812a264481d914ec7efb54cecfd9c 100644 (file)
@@ -714,20 +714,28 @@ static const char * const pedit_htype_str[] = {
        [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
 };
 
-static void print_pedit_location(FILE *f,
-                                enum pedit_header_type htype, __u32 off)
+static int print_pedit_location(FILE *f,
+                               enum pedit_header_type htype, __u32 off)
 {
-       if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
-               fprintf(f, "%d", (unsigned int)off);
-               return;
+       char *buf = NULL;
+       int rc;
+
+       if (htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
+               if (htype < ARRAY_SIZE(pedit_htype_str))
+                       rc = asprintf(&buf, "%s", pedit_htype_str[htype]);
+               else
+                       rc = asprintf(&buf, "unknown(%d)", htype);
+               if (rc < 0)
+                       return rc;
+               print_string(PRINT_ANY, "htype", "%s", buf);
+               print_int(PRINT_ANY, "offset", "%+d", off);
+       } else {
+               print_string(PRINT_JSON, "htype", NULL, "network");
+               print_int(PRINT_ANY, "offset", "%d", off);
        }
 
-       if (htype < ARRAY_SIZE(pedit_htype_str))
-               fprintf(f, "%s", pedit_htype_str[htype]);
-       else
-               fprintf(f, "unknown(%d)", htype);
-
-       fprintf(f, "%c%d", (int)off  >= 0 ? '+' : '-', abs((int)off));
+       free(buf);
+       return 0;
 }
 
 static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
@@ -735,6 +743,7 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
        struct tc_pedit_sel *sel;
        struct rtattr *tb[TCA_PEDIT_MAX + 1];
        struct m_pedit_key_ex *keys_ex = NULL;
+       int err;
 
        if (arg == NULL)
                return -1;
@@ -774,11 +783,12 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
                }
        }
 
-       fprintf(f, " pedit ");
+       print_string(PRINT_ANY, "kind", " %s ", "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);
+       print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
+       print_uint(PRINT_ANY, "index", " \t index %u", sel->index);
+       print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
+       print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
 
        if (show_stats) {
                if (tb[TCA_PEDIT_TM]) {
@@ -787,6 +797,7 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
                        print_tm(f, tm);
                }
        }
+       open_json_array(PRINT_JSON, "keys");
        if (sel->nkeys) {
                int i;
                struct tc_pedit_key *key = sel->keys;
@@ -804,21 +815,31 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
                                key_ex++;
                        }
 
-                       fprintf(f, "\n\t key #%d", i);
-
-                       fprintf(f, "  at ");
-
-                       print_pedit_location(f, htype, key->off);
-
-                       fprintf(f, ": %s %08x mask %08x",
-                               cmd ? "add" : "val",
-                               (unsigned int)ntohl(key->val),
-                               (unsigned int)ntohl(key->mask));
+                       open_json_object(NULL);
+                       print_uint(PRINT_FP, NULL, "\n\t key #%d  at ", i);
+
+                       err = print_pedit_location(f, htype, key->off);
+                       if (err)
+                               return err;
+
+                       /* In FP, report the "set" command as "val" to keep
+                        * backward compatibility. Report the true name in JSON.
+                        */
+                       print_string(PRINT_FP, NULL, ": %s",
+                                    cmd ? "add" : "val");
+                       print_string(PRINT_JSON, "cmd", NULL,
+                                    cmd ? "add" : "set");
+                       print_hex(PRINT_ANY, "val", " %08x",
+                                 (unsigned int)ntohl(key->val));
+                       print_hex(PRINT_ANY, "mask", " mask %08x",
+                                 (unsigned int)ntohl(key->mask));
+                       close_json_object();
                }
        } else {
                fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
                        sel->nkeys);
        }
+       close_json_array(PRINT_JSON, " ");
 
        print_nl();
 
index 1f6921f33547a75a09881a4a8ffb6f0561c99db6..bfec90724d728bf93e49b391c140637c495fba3e 100644 (file)
@@ -29,7 +29,7 @@ static void explain(void)
                "src_ip <IP> (mandatory)\n"
                "dst_ip <IP> (mandatory)\n"
                "dst_port <UDP_PORT>\n"
-               "geneve_opts <OPTIONS>\n"
+               "geneve_opts | vxlan_opts | erspan_opts <OPTIONS>\n"
                "csum | nocsum (default is \"csum\")\n");
 }
 
@@ -97,6 +97,21 @@ static int tunnel_key_parse_be16(char *str, int base, int type,
        return 0;
 }
 
+static int tunnel_key_parse_be32(char *str, int base, int type,
+                                struct nlmsghdr *n)
+{
+       __be32 value;
+       int ret;
+
+       ret = get_be32(&value, str, base);
+       if (ret)
+               return ret;
+
+       addattr32(n, MAX_MSG, type, value);
+
+       return 0;
+}
+
 static int tunnel_key_parse_u8(char *str, int base, int type,
                               struct nlmsghdr *n)
 {
@@ -112,6 +127,21 @@ static int tunnel_key_parse_u8(char *str, int base, int type,
        return 0;
 }
 
+static int tunnel_key_parse_u32(char *str, int base, int type,
+                               struct nlmsghdr *n)
+{
+       __u32 value;
+       int ret;
+
+       ret = get_u32(&value, str, base);
+       if (ret)
+               return ret;
+
+       addattr32(n, MAX_MSG, type, value);
+
+       return 0;
+}
+
 static int tunnel_key_parse_geneve_opt(char *str, struct nlmsghdr *n)
 {
        char *token, *saveptr = NULL;
@@ -190,6 +220,84 @@ static int tunnel_key_parse_geneve_opts(char *str, struct nlmsghdr *n)
        return 0;
 }
 
+static int tunnel_key_parse_vxlan_opt(char *str, struct nlmsghdr *n)
+{
+       struct rtattr *encap, *nest;
+       int ret;
+
+       encap = addattr_nest(n, MAX_MSG,
+                            TCA_TUNNEL_KEY_ENC_OPTS | NLA_F_NESTED);
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_TUNNEL_KEY_ENC_OPTS_VXLAN | NLA_F_NESTED);
+
+       ret = tunnel_key_parse_u32(str, 0,
+                                  TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, n);
+       if (ret)
+               return ret;
+
+       addattr_nest_end(n, nest);
+       addattr_nest_end(n, encap);
+
+       return 0;
+}
+
+static int tunnel_key_parse_erspan_opt(char *str, struct nlmsghdr *n)
+{
+       char *token, *saveptr = NULL;
+       struct rtattr *encap, *nest;
+       int i, ret;
+
+       encap = addattr_nest(n, MAX_MSG,
+                            TCA_TUNNEL_KEY_ENC_OPTS | NLA_F_NESTED);
+       nest = addattr_nest(n, MAX_MSG,
+                           TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN | NLA_F_NESTED);
+
+       token = strtok_r(str, ":", &saveptr);
+       i = 1;
+       while (token) {
+               switch (i) {
+               case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER:
+               {
+                       ret = tunnel_key_parse_u8(token, 0, i, n);
+                       if (ret)
+                               return ret;
+                       break;
+               }
+               case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX:
+               {
+                       ret = tunnel_key_parse_be32(token, 0, i, n);
+                       if (ret)
+                               return ret;
+                       break;
+               }
+               case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR:
+               {
+                       ret = tunnel_key_parse_u8(token, 0, i, n);
+                       if (ret)
+                               return ret;
+                       break;
+               }
+               case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID:
+               {
+                       ret = tunnel_key_parse_u8(token, 0, i, n);
+                       if (ret)
+                               return ret;
+                       break;
+               }
+               default:
+                       return -1;
+               }
+
+               token = strtok_r(NULL, ":", &saveptr);
+               i++;
+       }
+
+       addattr_nest_end(n, nest);
+       addattr_nest_end(n, encap);
+
+       return 0;
+}
+
 static int tunnel_key_parse_tos_ttl(char *str, int type, struct nlmsghdr *n)
 {
        int ret;
@@ -287,6 +395,20 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
                                fprintf(stderr, "Illegal \"geneve_opts\"\n");
                                return -1;
                        }
+               } else if (matches(*argv, "vxlan_opts") == 0) {
+                       NEXT_ARG();
+
+                       if (tunnel_key_parse_vxlan_opt(*argv, n)) {
+                               fprintf(stderr, "Illegal \"vxlan_opts\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "erspan_opts") == 0) {
+                       NEXT_ARG();
+
+                       if (tunnel_key_parse_erspan_opt(*argv, n)) {
+                               fprintf(stderr, "Illegal \"erspan_opts\"\n");
+                               return -1;
+                       }
                } else if (matches(*argv, "tos") == 0) {
                        NEXT_ARG();
                        ret = tunnel_key_parse_tos_ttl(*argv,
@@ -406,13 +528,13 @@ static void tunnel_key_print_flag(FILE *f, const char *name_on,
                     rta_getattr_u8(attr) ? name_on : name_off);
 }
 
-static void tunnel_key_print_geneve_options(const char *name,
-                                           struct rtattr *attr)
+static void tunnel_key_print_geneve_options(struct rtattr *attr)
 {
        struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
        struct rtattr *i = RTA_DATA(attr);
        int ii, data_len = 0, offset = 0;
        int rem = RTA_PAYLOAD(attr);
+       char *name = "geneve_opts";
        char strbuf[rem * 2 + 1];
        char data[rem * 2 + 1];
        uint8_t data_r[rem];
@@ -421,7 +543,7 @@ static void tunnel_key_print_geneve_options(const char *name,
 
        open_json_array(PRINT_JSON, name);
        print_nl();
-       print_string(PRINT_FP, name, "\t%s ", "geneve_opt");
+       print_string(PRINT_FP, name, "\t%s ", name);
 
        while (rem) {
                parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, i, rem);
@@ -454,7 +576,60 @@ static void tunnel_key_print_geneve_options(const char *name,
        close_json_array(PRINT_JSON, name);
 }
 
-static void tunnel_key_print_key_opt(const char *name, struct rtattr *attr)
+static void tunnel_key_print_vxlan_options(struct rtattr *attr)
+{
+       struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "vxlan_opts";
+       __u32 gbp;
+
+       parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX, i, rem);
+       gbp = rta_getattr_u32(tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP]);
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "gbp", "%u", gbp);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+}
+
+static void tunnel_key_print_erspan_options(struct rtattr *attr)
+{
+       struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1];
+       struct rtattr *i = RTA_DATA(attr);
+       int rem = RTA_PAYLOAD(attr);
+       char *name = "erspan_opts";
+       __u8 ver, hwid, dir;
+       __u32 idx;
+
+       parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, i, rem);
+       ver = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]);
+       if (ver == 1) {
+               idx = rta_getattr_be32(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]);
+               dir = 0;
+               hwid = 0;
+       } else {
+               idx = 0;
+               dir = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR]);
+               hwid = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]);
+       }
+
+       print_nl();
+       print_string(PRINT_FP, name, "\t%s ", name);
+       open_json_array(PRINT_JSON, name);
+       open_json_object(NULL);
+       print_uint(PRINT_ANY, "ver", "%u", ver);
+       print_uint(PRINT_ANY, "index", ":%u", idx);
+       print_uint(PRINT_ANY, "dir", ":%u", dir);
+       print_uint(PRINT_ANY, "hwid", ":%u", hwid);
+       close_json_object();
+       close_json_array(PRINT_JSON, name);
+}
+
+static void tunnel_key_print_key_opt(struct rtattr *attr)
 {
        struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1];
 
@@ -462,8 +637,15 @@ static void tunnel_key_print_key_opt(const char *name, struct rtattr *attr)
                return;
 
        parse_rtattr_nested(tb, TCA_TUNNEL_KEY_ENC_OPTS_MAX, attr);
-       tunnel_key_print_geneve_options(name,
-                                       tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]);
+       if (tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE])
+               tunnel_key_print_geneve_options(
+                       tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]);
+       else if (tb[TCA_TUNNEL_KEY_ENC_OPTS_VXLAN])
+               tunnel_key_print_vxlan_options(
+                       tb[TCA_TUNNEL_KEY_ENC_OPTS_VXLAN]);
+       else if (tb[TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN])
+               tunnel_key_print_erspan_options(
+                       tb[TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN]);
 }
 
 static void tunnel_key_print_tos_ttl(FILE *f, char *name,
@@ -519,8 +701,7 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
                                        tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
                tunnel_key_print_dst_port(f, "dst_port",
                                          tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
-               tunnel_key_print_key_opt("geneve_opts",
-                                        tb[TCA_TUNNEL_KEY_ENC_OPTS]);
+               tunnel_key_print_key_opt(tb[TCA_TUNNEL_KEY_ENC_OPTS]);
                tunnel_key_print_flag(f, "nocsum", "csum",
                                      tb[TCA_TUNNEL_KEY_NO_CSUM]);
                tunnel_key_print_tos_ttl(f, "tos",
index 0eb41308a3fbc7ebbbf2bcb6558723c8cd74eb64..f26ba8d7b708e8342790270dde443e04b0467408 100644 (file)
@@ -48,6 +48,7 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
        __u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
        __u16 shaper = TC_MQPRIO_SHAPER_DCB;
        __u16 mode = TC_MQPRIO_MODE_DCB;
+       int cnt_off_pairs = 0;
        struct rtattr *tail;
        __u32 flags = 0;
 
@@ -94,6 +95,7 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
                                }
                                free(tmp);
                                idx++;
+                               cnt_off_pairs++;
                        }
                } else if (strcmp(*argv, "hw") == 0) {
                        NEXT_ARG();
@@ -173,6 +175,12 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
                argc--; argv++;
        }
 
+       if (cnt_off_pairs > opt.num_tc) {
+               fprintf(stderr, "queues count/offset pair count %d can not be higher than given num_tc %d\n",
+                       cnt_off_pairs, opt.num_tc);
+               return -1;
+       }
+
        tail = NLMSG_TAIL(n);
        addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
 
index fd5fcb242b64214c0e498ea2c1cbc884760b00d0..b7ff911b63edac989528ea4042ea118c9279f8a7 100644 (file)
@@ -758,6 +758,10 @@ void print_tm(FILE *f, const struct tcf_t *tm)
                print_uint(PRINT_ANY, "last_used", " used %u sec",
                           tm->lastuse / hz);
 
+       if (tm->firstuse != 0)
+               print_uint(PRINT_ANY, "first_used", " firstused %u sec",
+                          tm->firstuse / hz);
+
        if (tm->expires != 0)
                print_uint(PRINT_ANY, "expires", " expires %u sec",
                           tm->expires / hz);
index e123c18635756917943dc6f48a97d592db7b4785..ba77a20152ea3ae3ace5ffc22256d88565a9085f 100644 (file)
@@ -334,7 +334,7 @@ static int _show_link_stat(const char *name, struct nlattr *attrs[],
 
        open_json_object(NULL);
 
-       print_string(PRINT_ANY, "link", "\nLink <%s>\n", name);
+       print_string(PRINT_ANY, "link", "Link <%s>\n", name);
        print_string(PRINT_JSON, "state", "", NULL);
        open_json_array(PRINT_JSON, NULL);
        if (attrs[TIPC_NLA_LINK_ACTIVE])
@@ -433,7 +433,7 @@ static int _show_link_stat(const char *name, struct nlattr *attrs[],
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
        print_uint(PRINT_ANY, "send queue max", "  Send queue max:%u",
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
-       print_uint(PRINT_ANY, "avg", " avg:%u\n",
+       print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
 
        close_json_object();
@@ -496,7 +496,7 @@ static int _show_bc_link_stat(const char *name, struct nlattr *prop[],
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
        print_uint(PRINT_ANY, "send queue max", "  Send queue max:%u",
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
-       print_uint(PRINT_ANY, "avg", " avg:%u\n",
+       print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
                           mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
        close_json_object();
 
@@ -527,8 +527,10 @@ static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
 
        name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
 
-       /* If a link is passed, skip all but that link */
-       if (link && (strcmp(name, link) != 0))
+       /* If a link is passed, skip all but that link.
+        * Support a substring matching as well.
+        */
+       if (link && !strstr(name, link))
                return MNL_CB_OK;
 
        if (attrs[TIPC_NLA_LINK_BROADCAST]) {
@@ -540,7 +542,7 @@ static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
 
 static void cmd_link_stat_show_help(struct cmdl *cmdl)
 {
-       fprintf(stderr, "Usage: %s link stat show [ link LINK ]\n",
+       fprintf(stderr, "Usage: %s link stat show [ link { LINK | SUBSTRING | all } ]\n",
                cmdl->argv[0]);
 }
 
@@ -554,6 +556,7 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
                { "link",               OPT_KEYVAL,     NULL },
                { NULL }
        };
+       struct nlattr *attrs;
        int err = 0;
 
        if (help_flag) {
@@ -571,8 +574,14 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
                return -EINVAL;
 
        opt = get_opt(opts, "link");
-       if (opt)
-               link = opt->val;
+       if (opt) {
+               if (strcmp(opt->val, "all"))
+                       link = opt->val;
+               /* Set the flag to dump all bc links */
+               attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+               mnl_attr_put(nlh, TIPC_NLA_LINK_BROADCAST, 0, NULL);
+               mnl_attr_nest_end(nlh, attrs);
+       }
 
        new_json_obj(json);
        err = msg_dumpit(nlh, link_stat_show_cb, link);