]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge branch 'iproute2-master' into iproute2-next
authorDavid Ahern <dsahern@gmail.com>
Wed, 24 Jan 2018 17:59:03 +0000 (09:59 -0800)
committerDavid Ahern <dsahern@gmail.com>
Wed, 24 Jan 2018 17:59:03 +0000 (09:59 -0800)
Signed-off-by: David Ahern <dsahern@gmail.com>
42 files changed:
include/libnetlink.h
include/uapi/linux/bpf.h
include/uapi/linux/can/netlink.h
include/uapi/linux/devlink.h
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
include/uapi/linux/if_macsec.h
include/uapi/linux/if_tun.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/inet_diag.h
include/uapi/linux/l2tp.h
include/uapi/linux/rtnetlink.h
include/uapi/linux/sctp.h
include/uapi/linux/tipc.h
include/utils.h
ip/Makefile
ip/ipaddress.c
ip/iplink.c
ip/iplink_geneve.c
ip/iplink_netdevsim.c [new file with mode: 0644]
ip/iplink_vxlan.c
ip/iproute_lwtunnel.c
ip/ipseg6.c
ip/link_gre.c
ip/link_gre6.c
lib/bpf.c
lib/libnetlink.c
lib/utils.c
man/man8/ip-link.8.in
man/man8/tc.8
rdma/dev.c
rdma/link.c
rdma/rdma.c
rdma/rdma.h
rdma/utils.c
tc/m_action.c
tc/tc.c
tc/tc_common.h
tc/tc_filter.c
tc/tc_qdisc.c
tc/tc_red.c
tc/tc_util.h

index a4d83b9ea69b5ba68cc39f14627ca69fb45db5a7..d6322190afaae57e219e60323999994d8d7cf2d4 100644 (file)
@@ -96,6 +96,9 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth,
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
              struct nlmsghdr **answer)
        __attribute__((warn_unused_result));
+int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen,
+                 struct nlmsghdr **answer)
+       __attribute__((warn_unused_result));
 int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
              struct nlmsghdr **answer, nl_ext_ack_fn_t errfn)
        __attribute__((warn_unused_result));
index 81f9145fc5a633627e87cc19ddf64ff73efe80b0..a902a490bc19cd275064c8bab4019fe27295c9e1 100644 (file)
@@ -197,8 +197,14 @@ enum bpf_attach_type {
  */
 #define BPF_F_STRICT_ALIGNMENT (1U << 0)
 
+/* when bpf_ldimm64->src_reg == BPF_PSEUDO_MAP_FD, bpf_ldimm64->imm == fd */
 #define BPF_PSEUDO_MAP_FD      1
 
+/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
+ * offset to another bpf function
+ */
+#define BPF_PSEUDO_CALL                1
+
 /* flags for BPF_MAP_UPDATE_ELEM command */
 #define BPF_ANY                0 /* create new element or update existing */
 #define BPF_NOEXIST    1 /* create new element if it didn't exist */
@@ -239,6 +245,7 @@ union bpf_attr {
                                         * BPF_F_NUMA_NODE is set).
                                         */
                char    map_name[BPF_OBJ_NAME_LEN];
+               __u32   map_ifindex;    /* ifindex of netdev to create on */
        };
 
        struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
@@ -677,6 +684,10 @@ union bpf_attr {
  *     @buf: buf to fill
  *     @buf_size: size of the buf
  *     Return : 0 on success or negative error code
+ *
+ * int bpf_override_return(pt_regs, rc)
+ *     @pt_regs: pointer to struct pt_regs
+ *     @rc: the return value to set
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -736,7 +747,8 @@ union bpf_attr {
        FN(xdp_adjust_meta),            \
        FN(perf_event_read_value),      \
        FN(perf_prog_read_value),       \
-       FN(getsockopt),
+       FN(getsockopt),                 \
+       FN(override_return),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -888,6 +900,9 @@ struct xdp_md {
        __u32 data;
        __u32 data_end;
        __u32 data_meta;
+       /* Below access go through struct xdp_rxq_info */
+       __u32 ingress_ifindex; /* rxq->dev->ifindex */
+       __u32 rx_queue_index;  /* rxq->queue_index  */
 };
 
 enum sk_action {
@@ -910,6 +925,9 @@ struct bpf_prog_info {
        __u32 nr_map_ids;
        __aligned_u64 map_ids;
        char name[BPF_OBJ_NAME_LEN];
+       __u32 ifindex;
+       __u64 netns_dev;
+       __u64 netns_ino;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -941,6 +959,12 @@ struct bpf_sock_ops {
        __u32 local_ip6[4];     /* Stored in network byte order */
        __u32 remote_port;      /* Stored in network byte order */
        __u32 local_port;       /* stored in host byte order */
+       __u32 is_fullsock;      /* Some TCP fields are only valid if
+                                * there is a full socket. If not, the
+                                * fields read as zero.
+                                */
+       __u32 snd_cwnd;
+       __u32 srtt_us;          /* Averaged RTT << 3 in usecs */
 };
 
 /* List of known BPF sock_ops operators.
@@ -995,7 +1019,8 @@ struct bpf_perf_event_value {
 #define BPF_DEVCG_DEV_CHAR     (1ULL << 1)
 
 struct bpf_cgroup_dev_ctx {
-       __u32 access_type; /* (access << 16) | type */
+       /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */
+       __u32 access_type;
        __u32 major;
        __u32 minor;
 };
index c12e9bf088ef666c52e3adf528b45063fa04a468..f0c5e58b8ee7652d25920c193145021a9adac3e2 100644 (file)
@@ -132,6 +132,7 @@ enum {
        IFLA_CAN_TERMINATION_CONST,
        IFLA_CAN_BITRATE_CONST,
        IFLA_CAN_DATA_BITRATE_CONST,
+       IFLA_CAN_BITRATE_MAX,
        __IFLA_CAN_MAX
 };
 
index 0c44d09daeb5582c6f4e74b736fa7be3948a28a0..9f17286ec89f76a469d1457be8ebbd7ba75c67dc 100644 (file)
@@ -70,6 +70,13 @@ enum devlink_command {
        DEVLINK_CMD_DPIPE_ENTRIES_GET,
        DEVLINK_CMD_DPIPE_HEADERS_GET,
        DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET,
+       DEVLINK_CMD_RESOURCE_SET,
+       DEVLINK_CMD_RESOURCE_DUMP,
+
+       /* Hot driver reload, makes configuration changes take place. The
+        * devlink instance is not released during the process.
+        */
+       DEVLINK_CMD_RELOAD,
 
        /* add new commands above here */
        __DEVLINK_CMD_MAX,
@@ -202,6 +209,20 @@ enum devlink_attr {
        DEVLINK_ATTR_PAD,
 
        DEVLINK_ATTR_ESWITCH_ENCAP_MODE,        /* u8 */
+       DEVLINK_ATTR_RESOURCE_LIST,             /* nested */
+       DEVLINK_ATTR_RESOURCE,                  /* nested */
+       DEVLINK_ATTR_RESOURCE_NAME,             /* string */
+       DEVLINK_ATTR_RESOURCE_ID,               /* u64 */
+       DEVLINK_ATTR_RESOURCE_SIZE,             /* u64 */
+       DEVLINK_ATTR_RESOURCE_SIZE_NEW,         /* u64 */
+       DEVLINK_ATTR_RESOURCE_SIZE_VALID,       /* u8 */
+       DEVLINK_ATTR_RESOURCE_SIZE_MIN,         /* u64 */
+       DEVLINK_ATTR_RESOURCE_SIZE_MAX,         /* u64 */
+       DEVLINK_ATTR_RESOURCE_SIZE_GRAN,        /* u64 */
+       DEVLINK_ATTR_RESOURCE_UNIT,             /* u8 */
+       DEVLINK_ATTR_RESOURCE_OCC,              /* u64 */
+       DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID,   /* u64 */
+       DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,/* u64 */
 
        /* add new attributes above here, update the policy in devlink.c */
 
@@ -245,4 +266,8 @@ enum devlink_dpipe_header_id {
        DEVLINK_DPIPE_HEADER_IPV6,
 };
 
+enum devlink_resource_unit {
+       DEVLINK_RESOURCE_UNIT_ENTRY,
+};
+
 #endif /* _LINUX_DEVLINK_H_ */
index 5be262f400e521a467a81aa9cdae18164a3f9cb0..5f1d4af33a111423b61b712a8969da67d86a298f 100644 (file)
@@ -48,6 +48,7 @@
 #define ETH_P_PUP      0x0200          /* Xerox PUP packet             */
 #define ETH_P_PUPAT    0x0201          /* Xerox PUP Addr Trans packet  */
 #define ETH_P_TSN      0x22F0          /* TSN (IEEE 1722) packet       */
+#define ETH_P_ERSPAN2  0x22EB          /* ERSPAN version 2 (type III)  */
 #define ETH_P_IP       0x0800          /* Internet Protocol packet     */
 #define ETH_P_X25      0x0805          /* CCITT X.25                   */
 #define ETH_P_ARP      0x0806          /* Address Resolution packet    */
index dab52465bd5f8f451da25abece2b34b7058cacfe..1726e49fbc6b8482a8a1a1165b7f80988f410709 100644 (file)
@@ -730,6 +730,8 @@ enum {
        IFLA_VF_STATS_BROADCAST,
        IFLA_VF_STATS_MULTICAST,
        IFLA_VF_STATS_PAD,
+       IFLA_VF_STATS_RX_DROPPED,
+       IFLA_VF_STATS_TX_DROPPED,
        __IFLA_VF_STATS_MAX,
 };
 
index 560ab0070d2e0b0b69a8d9d2681097bd04f468ad..0cfb902a09acc64dab1fdcae0a0346138f33d4a3 100644 (file)
 #define MACSEC_GENL_NAME "macsec"
 #define MACSEC_GENL_VERSION 1
 
-#define MACSEC_MAX_KEY_LEN 128
+#define MACSEC_MAX_KEY_LEN 256
 
 #define MACSEC_KEYID_LEN 16
 
-#define MACSEC_DEFAULT_CIPHER_ID   0x0080020001000001ULL
-#define MACSEC_DEFAULT_CIPHER_ALT  0x0080C20001000001ULL
+/* cipher IDs as per IEEE802.1AEbn-2011 */
+#define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL
+#define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL
+
+#define MACSEC_DEFAULT_CIPHER_ID     MACSEC_CIPHER_ID_GCM_AES_128
+/* deprecated cipher ID for GCM-AES-128 */
+#define MACSEC_DEFAULT_CIPHER_ALT    0x0080020001000001ULL
 
 #define MACSEC_MIN_ICV_LEN 8
 #define MACSEC_MAX_ICV_LEN 32
index a0e25624e421d845ecaaac41aafeda2d663166ad..be9b744a164589541e89c57c2d5cdada7dc0743e 100644 (file)
@@ -57,6 +57,8 @@
  */
 #define TUNSETVNETBE _IOW('T', 222, int)
 #define TUNGETVNETBE _IOR('T', 223, int)
+#define TUNSETSTEERINGEBPF _IOR('T', 224, int)
+#define TUNSETFILTEREBPF _IOR('T', 225, int)
 
 /* TUNSETIFF ifr flags */
 #define IFF_TUN                0x0001
index 38cdf90692f86b9869696e2c67063db964a22da9..ecdc76669cfdde02f37158cbec5fbc681b3fc6ba 100644 (file)
@@ -137,6 +137,9 @@ enum {
        IFLA_GRE_IGNORE_DF,
        IFLA_GRE_FWMARK,
        IFLA_GRE_ERSPAN_INDEX,
+       IFLA_GRE_ERSPAN_VER,
+       IFLA_GRE_ERSPAN_DIR,
+       IFLA_GRE_ERSPAN_HWID,
        __IFLA_GRE_MAX,
 };
 
index 8a22e17e46685e5dd1c3f2aadadbf9828f5f131e..f98d82d4b2c033b5fdc74d525db08360131c1408 100644 (file)
@@ -92,6 +92,8 @@ enum {
        INET_DIAG_BC_D_COND,
        INET_DIAG_BC_DEV_COND,   /* u32 ifindex */
        INET_DIAG_BC_MARK_COND,
+       INET_DIAG_BC_S_EQ,
+       INET_DIAG_BC_D_EQ,
 };
 
 struct inet_diag_hostcond {
index 472e992471be8132c31b86e6ae35beb157ef3798..1fe52a7dd4a94c04cdb8a64c3ee4a8b66cbad9c6 100644 (file)
@@ -65,7 +65,7 @@ struct sockaddr_l2tpip6 {
  * TUNNEL_MODIFY       - CONN_ID, udpcsum
  * TUNNEL_GETSTATS     - CONN_ID, (stats)
  * TUNNEL_GET          - CONN_ID, (...)
- * SESSION_CREATE      - SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec
+ * SESSION_CREATE      - SESSION_ID, PW_TYPE, data_seq, cookie, peer_cookie, l2spec
  * SESSION_DELETE      - SESSION_ID
  * SESSION_MODIFY      - SESSION_ID, data_seq
  * SESSION_GET         - SESSION_ID, (...)
@@ -94,10 +94,10 @@ enum {
        L2TP_ATTR_NONE,                 /* no data */
        L2TP_ATTR_PW_TYPE,              /* u16, enum l2tp_pwtype */
        L2TP_ATTR_ENCAP_TYPE,           /* u16, enum l2tp_encap_type */
-       L2TP_ATTR_OFFSET,               /* u16 */
+       L2TP_ATTR_OFFSET,               /* u16 (not used) */
        L2TP_ATTR_DATA_SEQ,             /* u16 */
        L2TP_ATTR_L2SPEC_TYPE,          /* u8, enum l2tp_l2spec_type */
-       L2TP_ATTR_L2SPEC_LEN,           /* u8, enum l2tp_l2spec_type */
+       L2TP_ATTR_L2SPEC_LEN,           /* u8 (not used) */
        L2TP_ATTR_PROTO_VERSION,        /* u8 */
        L2TP_ATTR_IFNAME,               /* string */
        L2TP_ATTR_CONN_ID,              /* u32 */
index 13bf56fcf8c7826b6bd9f8e41db34d28574f5b14..aba8b18e4ed684417c740fdb9527dd8a6b9cbc18 100644 (file)
@@ -541,9 +541,19 @@ struct tcmsg {
        int             tcm_ifindex;
        __u32           tcm_handle;
        __u32           tcm_parent;
+/* tcm_block_index is used instead of tcm_parent
+ * in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK
+ */
+#define tcm_block_index tcm_parent
        __u32           tcm_info;
 };
 
+/* For manipulation of filters in shared block, tcm_ifindex is set to
+ * TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index
+ * which is the block index.
+ */
+#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
+
 enum {
        TCA_UNSPEC,
        TCA_KIND,
@@ -558,6 +568,8 @@ enum {
        TCA_DUMP_INVISIBLE,
        TCA_CHAIN,
        TCA_HW_OFFLOAD,
+       TCA_INGRESS_BLOCK,
+       TCA_EGRESS_BLOCK,
        __TCA_MAX
 };
 
index c5cff287518904ddd9429da6f05798d3773f9ab6..64309a2b73cb326c74200e3ffda62f2ff7f9258e 100644 (file)
@@ -125,6 +125,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_SOCKOPT_PEELOFF_FLAGS 122
 #define SCTP_STREAM_SCHEDULER  123
 #define SCTP_STREAM_SCHEDULER_VALUE    124
+#define SCTP_INTERLEAVING_SUPPORTED    125
 
 /* PR-SCTP policies */
 #define SCTP_PR_SCTP_NONE      0x0000
@@ -459,6 +460,8 @@ struct sctp_pdapi_event {
        __u32 pdapi_length;
        __u32 pdapi_indication;
        sctp_assoc_t pdapi_assoc_id;
+       __u32 pdapi_stream;
+       __u32 pdapi_seq;
 };
 
 enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
index f43ddaf39c0cf419ce89c2610908cbff78240992..cd22b9b2f77963dc99fd2ee5013d8a7a0c22eddc 100644 (file)
@@ -117,10 +117,9 @@ static __inline__ unsigned int tipc_node(__u32 addr)
 /*
  * Publication scopes when binding port names and port name sequences
  */
-
-#define TIPC_ZONE_SCOPE                1
-#define TIPC_CLUSTER_SCOPE     2
-#define TIPC_NODE_SCOPE                3
+#define TIPC_ZONE_SCOPE         1
+#define TIPC_CLUSTER_SCOPE      2
+#define TIPC_NODE_SCOPE         3
 
 /*
  * Limiting values for messages
index d3895d5627262185616534c6e9513a669e886a08..f562547d69837871b948722abfb0d87d8df1ea19 100644 (file)
@@ -54,7 +54,40 @@ typedef struct
        __u32 data[64];
 } inet_prefix;
 
-#define PREFIXLEN_SPECIFIED 1
+enum {
+       PREFIXLEN_SPECIFIED     = (1 << 0),
+       ADDRTYPE_INET           = (1 << 1),
+       ADDRTYPE_UNSPEC         = (1 << 2),
+       ADDRTYPE_MULTI          = (1 << 3),
+
+       ADDRTYPE_INET_UNSPEC    = ADDRTYPE_INET | ADDRTYPE_UNSPEC,
+       ADDRTYPE_INET_MULTI     = ADDRTYPE_INET | ADDRTYPE_MULTI
+};
+
+static inline bool is_addrtype_inet(const inet_prefix *p)
+{
+       return p->flags & ADDRTYPE_INET;
+}
+
+static inline bool is_addrtype_inet_unspec(const inet_prefix *p)
+{
+       return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET_UNSPEC;
+}
+
+static inline bool is_addrtype_inet_multi(const inet_prefix *p)
+{
+       return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET_MULTI;
+}
+
+static inline bool is_addrtype_inet_not_unspec(const inet_prefix *p)
+{
+       return (p->flags & ADDRTYPE_INET_UNSPEC) == ADDRTYPE_INET;
+}
+
+static inline bool is_addrtype_inet_not_multi(const inet_prefix *p)
+{
+       return (p->flags & ADDRTYPE_INET_MULTI) == ADDRTYPE_INET;
+}
 
 #define DN_MAXADDL 20
 #ifndef AF_DECnet
@@ -236,7 +269,6 @@ void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n);
 extern int cmdlineno;
 ssize_t getcmdline(char **line, size_t *len, FILE *in);
 int makeargs(char *line, char *argv[], int maxargs);
-int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6);
 
 struct iplink_req {
        struct nlmsghdr         n;
index a653c1bdd9673ee267437dea31850128af4c6665..77fadeed2e62bda3d0cadf345ddb8bca56efc3e5 100644 (file)
@@ -10,7 +10,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.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
+    ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o
 
 RTMONOBJ=rtmon.o
 
index c41e24b50d24f4d6f4925af1a0d431c6fa962c7b..ba60125c1b78dd31eee342f5bd2f2fdb719f4ebd 100644 (file)
@@ -78,7 +78,7 @@ static void usage(void)
        fprintf(stderr, "          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n");
        fprintf(stderr, "          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n");
        fprintf(stderr, "          nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n");
-       fprintf(stderr, "          hsr | macsec\n");
+       fprintf(stderr, "          hsr | macsec | netdevsim\n");
 
        exit(-1);
 }
index 2db2c691c1f918322f760f74362be6774a3e801e..230f4c5377d9fee8e34a7c625aef536b72455201 100644 (file)
 #define LIBDIR "/usr/lib"
 #endif
 
+#ifndef GSO_MAX_SIZE
+#define GSO_MAX_SIZE           65536
+#endif
+#ifndef GSO_MAX_SEGS
+#define GSO_MAX_SEGS           65535
+#endif
+
+
 static void usage(void) __attribute__((noreturn));
 static int iplink_have_newlink(void);
 
@@ -97,7 +105,8 @@ void iplink_usage(void)
                "                         [ master DEVICE ][ vrf NAME ]\n"
                "                         [ nomaster ]\n"
                "                         [ addrgenmode { eui64 | none | stable_secret | random } ]\n"
-               "                         [ protodown { on | off } ]\n"
+               "                         [ protodown { on | off } ]\n"
+               "                         [ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n"
                "\n"
                "       ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n");
 
@@ -113,7 +122,7 @@ void iplink_usage(void)
                        "          bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n"
                        "          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n"
                        "          vti | nlmon | team_slave | bond_slave | ipvlan | geneve |\n"
-                       "          bridge_slave | vrf | macsec }\n");
+                       "          bridge_slave | vrf | macsec | netdevsim }\n");
        }
        exit(-1);
 }
@@ -861,6 +870,26 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
                                return on_off("protodown", *argv);
                        addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
                                 proto_down);
+               } else if (strcmp(*argv, "gso_max_size") == 0) {
+                       unsigned int max_size;
+
+                       NEXT_ARG();
+                       if (get_unsigned(&max_size, *argv, 0) ||
+                           max_size > GSO_MAX_SIZE)
+                               invarg("Invalid \"gso_max_size\" value\n",
+                                      *argv);
+                       addattr32(&req->n, sizeof(*req),
+                                 IFLA_GSO_MAX_SIZE, max_size);
+               } else if (strcmp(*argv, "gso_max_segs") == 0) {
+                       unsigned int max_segs;
+
+                       NEXT_ARG();
+                       if (get_unsigned(&max_segs, *argv, 0) ||
+                           max_segs > GSO_MAX_SEGS)
+                               invarg("Invalid \"gso_max_segs\" value\n",
+                                      *argv);
+                       addattr32(&req->n, sizeof(*req),
+                                 IFLA_GSO_MAX_SEGS, max_segs);
                } else {
                        if (matches(*argv, "help") == 0)
                                usage();
index 2d0a0411c4bbf896d0eee718f57aa9de73294d16..1c28bb9cb5e0c7ae2d2632331e7d0d6d74c8b3ef 100644 (file)
@@ -57,9 +57,8 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
                          struct nlmsghdr *n)
 {
+       inet_prefix daddr;
        __u32 vni = 0;
-       __u32 daddr = 0;
-       struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
        __u32 label = 0;
        __u8 ttl = 0;
        __u8 tos = 0;
@@ -72,6 +71,8 @@ static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
        bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
                       !(n->nlmsg_flags & NLM_F_CREATE));
 
+       daddr.flags = 0;
+
        while (argc > 0) {
                if (!matches(*argv, "id") ||
                    !matches(*argv, "vni")) {
@@ -84,11 +85,8 @@ static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
                        NEXT_ARG();
                        check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote",
                                     *argv);
-                       if (!inet_get_addr(*argv, &daddr, &daddr6)) {
-                               fprintf(stderr, "Invalid address \"%s\"\n", *argv);
-                               return -1;
-                       }
-                       if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
+                       get_addr(&daddr, *argv, AF_UNSPEC);
+                       if (!is_addrtype_inet_not_multi(&daddr))
                                invarg("invalid remote address", *argv);
                } else if (!matches(*argv, "ttl") ||
                           !matches(*argv, "hoplimit")) {
@@ -191,18 +189,17 @@ static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
                 * ID (VNI) to identify the geneve device, and we do not need
                 * the remote IP.
                 */
-               if (!set_op && !daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
+               if (!set_op && !is_addrtype_inet(&daddr)) {
                        fprintf(stderr, "geneve: remote link partner not specified\n");
                        return -1;
                }
        }
 
        addattr32(n, 1024, IFLA_GENEVE_ID, vni);
-       if (daddr)
-               addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4);
-       if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
-               addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6,
-                         sizeof(struct in6_addr));
+       if (is_addrtype_inet(&daddr)) {
+               int type = (daddr.family == AF_INET) ? IFLA_GENEVE_REMOTE :
+                                                      IFLA_GENEVE_REMOTE6;
+               addattr_l(n, sizeof(1024), type, daddr.data, daddr.bytelen);
        }
        if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL))
                addattr32(n, 1024, IFLA_GENEVE_LABEL, label);
diff --git a/ip/iplink_netdevsim.c b/ip/iplink_netdevsim.c
new file mode 100644 (file)
index 0000000..3448608
--- /dev/null
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+static void netdevsim_print_help(struct link_util *lu,
+                                int argc, char **argv, FILE *f)
+{
+       fprintf(f, "Usage: ... netdevsim\n");
+}
+
+struct link_util netdevsim_link_util = {
+       .id             = "netdevsim",
+       .print_help     = netdevsim_print_help,
+};
index 7ea908e6c8912b89cf0c8b3de18a0599179bf3dd..ad03ffb640cf18acee5b7336d6a17bc2e5a7c566 100644 (file)
@@ -74,11 +74,9 @@ static void check_duparg(__u64 *attrs, int type, const char *key,
 static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
                          struct nlmsghdr *n)
 {
+       inet_prefix saddr;
+       inet_prefix daddr;
        __u32 vni = 0;
-       __u32 gaddr = 0;
-       __u32 daddr = 0;
-       struct in6_addr gaddr6 = IN6ADDR_ANY_INIT;
-       struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
        __u8 learning = 1;
        __u16 dstport = 0;
        __u8 metadata = 0;
@@ -86,6 +84,9 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
        bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
                       !(n->nlmsg_flags & NLM_F_CREATE));
 
+       saddr.family = daddr.family = AF_UNSPEC;
+       saddr.flags = daddr.flags = 0;
+
        while (argc > 0) {
                if (!matches(*argv, "id") ||
                    !matches(*argv, "vni")) {
@@ -98,54 +99,33 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
                            vni >= 1u << 24)
                                invarg("invalid id", *argv);
                } else if (!matches(*argv, "group")) {
-                       if (daddr || !IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
+                       if (is_addrtype_inet_not_multi(&daddr)) {
                                fprintf(stderr, "vxlan: both group and remote");
                                fprintf(stderr, " cannot be specified\n");
                                return -1;
                        }
                        NEXT_ARG();
                        check_duparg(&attrs, IFLA_VXLAN_GROUP, "group", *argv);
-                       if (!inet_get_addr(*argv, &gaddr, &gaddr6)) {
-                               fprintf(stderr, "Invalid address \"%s\"\n", *argv);
-                               return -1;
-                       }
-                       if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr)))
+                       get_addr(&daddr, *argv, saddr.family);
+                       if (!is_addrtype_inet_multi(&daddr))
                                invarg("invalid group address", *argv);
                } else if (!matches(*argv, "remote")) {
-                       if (gaddr || !IN6_IS_ADDR_UNSPECIFIED(&gaddr6)) {
+                       if (is_addrtype_inet_multi(&daddr)) {
                                fprintf(stderr, "vxlan: both group and remote");
                                fprintf(stderr, " cannot be specified\n");
                                return -1;
                        }
                        NEXT_ARG();
                        check_duparg(&attrs, IFLA_VXLAN_GROUP, "remote", *argv);
-                       if (!inet_get_addr(*argv, &daddr, &daddr6)) {
-                               fprintf(stderr, "Invalid address \"%s\"\n", *argv);
-                               return -1;
-                       }
-                       if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
+                       get_addr(&daddr, *argv, saddr.family);
+                       if (!is_addrtype_inet_not_multi(&daddr))
                                invarg("invalid remote address", *argv);
                } else if (!matches(*argv, "local")) {
-                       __u32 saddr = 0;
-                       struct in6_addr saddr6 = IN6ADDR_ANY_INIT;
-
                        NEXT_ARG();
                        check_duparg(&attrs, IFLA_VXLAN_LOCAL, "local", *argv);
-                       if (strcmp(*argv, "any")) {
-                               if (!inet_get_addr(*argv, &saddr, &saddr6)) {
-                                       fprintf(stderr, "Invalid address \"%s\"\n", *argv);
-                                       return -1;
-                               }
-                       }
-
-                       if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6))
+                       get_addr(&saddr, *argv, daddr.family);
+                       if (!is_addrtype_inet_not_multi(&saddr))
                                invarg("invalid local address", *argv);
-
-                       if (saddr)
-                               addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
-                       else if (!IN6_IS_ADDR_UNSPECIFIED(&saddr6))
-                               addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6,
-                                         sizeof(struct in6_addr));
                } else if (!matches(*argv, "dev")) {
                        unsigned int link;
 
@@ -350,7 +330,7 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
                return -1;
        }
 
-       if ((gaddr || !IN6_IS_ADDR_UNSPECIFIED(&gaddr6)) &&
+       if (is_addrtype_inet_multi(&daddr) &&
            !VXLAN_ATTRSET(attrs, IFLA_VXLAN_LINK)) {
                fprintf(stderr, "vxlan: 'group' requires 'dev' to be specified\n");
                return -1;
@@ -369,18 +349,18 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
 
        if (VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID))
                addattr32(n, 1024, IFLA_VXLAN_ID, vni);
-       if (gaddr)
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
-       else if (daddr)
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
-       else if (!IN6_IS_ADDR_UNSPECIFIED(&gaddr6))
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr));
-       else if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6))
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
-       else if (preferred_family == AF_INET)
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
-       else if (preferred_family == AF_INET6)
-               addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
+
+       if (is_addrtype_inet(&saddr)) {
+               int type = (saddr.family == AF_INET) ? IFLA_VXLAN_LOCAL
+                                                    : IFLA_VXLAN_LOCAL6;
+               addattr_l(n, 1024, type, saddr.data, saddr.bytelen);
+       }
+
+       if (is_addrtype_inet(&daddr)) {
+               int type = (daddr.family == AF_INET) ? IFLA_VXLAN_GROUP
+                                                    : IFLA_VXLAN_GROUP6;
+               addattr_l(n, 1024, type, daddr.data, daddr.bytelen);
+       }
 
        if (!set_op || VXLAN_ATTRSET(attrs, IFLA_VXLAN_LEARNING))
                addattr8(n, 1024, IFLA_VXLAN_LEARNING, learning);
index da6ebb8e04cd5b133675aabb71bb6742c9e1dc16..ffa897a3f3dd1c76ef399cffddabee12aafcc0d4 100644 (file)
@@ -431,7 +431,10 @@ static struct ipv6_sr_hdr *parse_srh(char *segbuf, int hmac, bool encap)
 
        i = srh->first_segment;
        for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) {
-               inet_get_addr(s, NULL, &srh->segments[i]);
+               inet_prefix addr;
+
+               get_addr(&addr, s, AF_INET6);
+               memcpy(&srh->segments[i], addr.data, sizeof(struct in6_addr));
                i--;
        }
 
index 461a3c1c15d45a97be7067d359367fb968859777..e3ab31a516b83e55f284acbc63c6d37a1c75dd08 100644 (file)
@@ -49,7 +49,7 @@ static int genl_family = -1;
 
 static struct {
        unsigned int cmd;
-       struct in6_addr addr;
+       inet_prefix addr;
        __u32 keyid;
        const char *pass;
        __u8 alg_id;
@@ -152,7 +152,7 @@ static int seg6_do_cmd(void)
                break;
        }
        case SEG6_CMD_SET_TUNSRC:
-               addattr_l(&req.n, sizeof(req), SEG6_ATTR_DST, &opts.addr,
+               addattr_l(&req.n, sizeof(req), SEG6_ATTR_DST, opts.addr.data,
                          sizeof(struct in6_addr));
                break;
        case SEG6_CMD_DUMPHMAC:
@@ -226,9 +226,7 @@ int do_seg6(int argc, char **argv)
                } else if (matches(*argv, "set") == 0) {
                        NEXT_ARG();
                        opts.cmd = SEG6_CMD_SET_TUNSRC;
-                       if (!inet_get_addr(*argv, NULL, &opts.addr))
-                               invarg("tunsrc ADDRESS value is invalid",
-                                      *argv);
+                       get_addr(&opts.addr, *argv, AF_INET6);
                } else {
                        invarg("unknown", *argv);
                }
index 512695d825919c75f2d866fdf05f6d579b33abae..850eddb74cd6a008960685d3c984de00ad43d8be 100644 (file)
@@ -45,7 +45,11 @@ static void print_usage(FILE *f)
                "                            [ [no]encap-remcsum ]\n"
                "                            [ external ]\n"
                "                            [ fwmark MARK ]\n"
+               "                            [ erspan_ver version ]\n"
                "                            [ erspan IDX ]\n"
+               "                            [ erspan_dir { ingress | egress } ]\n"
+               "                            [ erspan_hwid hwid ]\n"
+               "                            [ external ]\n"
                "\n"
                "Where: ADDR := { IP_ADDRESS | any }\n"
                "       TOS  := { NUMBER | inherit }\n"
@@ -99,6 +103,9 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
        __u8 ignore_df = 0;
        __u32 fwmark = 0;
        __u32 erspan_idx = 0;
+       __u8 erspan_ver = 0;
+       __u8 erspan_dir = 0;
+       __u16 erspan_hwid = 0;
 
        if (!(n->nlmsg_flags & NLM_F_CREATE)) {
                if (rtnl_talk(&rth, &req.n, &answer) < 0) {
@@ -179,6 +186,15 @@ get_failed:
                if (greinfo[IFLA_GRE_ERSPAN_INDEX])
                        erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
 
+               if (greinfo[IFLA_GRE_ERSPAN_VER])
+                       erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]);
+
+               if (greinfo[IFLA_GRE_ERSPAN_DIR])
+                       erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]);
+
+               if (greinfo[IFLA_GRE_ERSPAN_HWID])
+                       erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]);
+
                free(answer);
        }
 
@@ -302,6 +318,24 @@ get_failed:
                                invarg("invalid erspan index\n", *argv);
                        if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
                                invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
+               } else if (strcmp(*argv, "erspan_ver") == 0) {
+                       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);
+               } else if (strcmp(*argv, "erspan_dir") == 0) {
+                       NEXT_ARG();
+                       if (matches(*argv, "ingress") == 0)
+                               erspan_dir = 0;
+                       else if (matches(*argv, "egress") == 0)
+                               erspan_dir = 1;
+                       else
+                               invarg("Invalid erspan direction.", *argv);
+               } else if (strcmp(*argv, "erspan_hwid") == 0) {
+                       NEXT_ARG();
+                       if (get_u16(&erspan_hwid, *argv, 0))
+                               invarg("invalid erspan hwid\n", *argv);
                } else
                        usage();
                argc--; argv++;
@@ -333,8 +367,15 @@ get_failed:
                addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
                addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
                addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
-               if (erspan_idx != 0)
-                       addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
+               if (erspan_ver) {
+                       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);
+                       } else if (erspan_ver == 2) {
+                               addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
+                               addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
+                       }
+               }
        } else {
                addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
        }
@@ -465,6 +506,29 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
                           "erspan_index", "erspan_index %u ", erspan_idx);
        }
 
+       if (tb[IFLA_GRE_ERSPAN_VER]) {
+               __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
+
+               print_uint(PRINT_ANY, "erspan_ver", "erspan_ver %u ", erspan_ver);
+       }
+
+       if (tb[IFLA_GRE_ERSPAN_DIR]) {
+               __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
+
+               if (erspan_dir == 0)
+                       print_string(PRINT_ANY, "erspan_dir",
+                                    "erspan_dir ingress ", NULL);
+               else
+                       print_string(PRINT_ANY, "erspan_dir",
+                                    "erspan_dir egress ", NULL);
+       }
+
+       if (tb[IFLA_GRE_ERSPAN_HWID]) {
+               __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
+
+               print_hex(PRINT_ANY, "erspan_hwid", "erspan_hwid 0x%x ", erspan_hwid);
+       }
+
        tnl_print_encap(tb,
                        IFLA_GRE_ENCAP_TYPE,
                        IFLA_GRE_ENCAP_FLAGS,
index 29846cf13a1002b881968c7f7743b8e11cffb3fb..bb196494cde65edeba940dcf9db8c3b3d57928b5 100644 (file)
@@ -52,7 +52,11 @@ static void print_usage(FILE *f)
                "                                  [ [no]encap-csum ]\n"
                "                                  [ [no]encap-csum6 ]\n"
                "                                  [ [no]encap-remcsum ]\n"
+               "                                  [ erspan_ver version ]\n"
                "                                  [ erspan IDX ]\n"
+               "                                  [ erspan_dir { ingress | egress } ]\n"
+               "                                  [ erspan_hwid hwid ]\n"
+               "                                  [ external ]\n"
                "\n"
                "Where: ADDR      := IPV6_ADDRESS\n"
                "       TTL       := { 0..255 } (default=%d)\n"
@@ -105,9 +109,13 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
        __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6;
        __u16 encapsport = 0;
        __u16 encapdport = 0;
+       __u8 metadata = 0;
        int len;
        __u32 fwmark = 0;
        __u32 erspan_idx = 0;
+       __u8 erspan_ver = 0;
+       __u8 erspan_dir = 0;
+       __u16 erspan_hwid = 0;
 
        if (!(n->nlmsg_flags & NLM_F_CREATE)) {
                if (rtnl_talk(&rth, &req.n, &answer) < 0) {
@@ -177,6 +185,9 @@ get_failed:
                if (greinfo[IFLA_GRE_ENCAP_SPORT])
                        encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
 
+               if (greinfo[IFLA_GRE_COLLECT_METADATA])
+                       metadata = 1;
+
                if (greinfo[IFLA_GRE_ENCAP_DPORT])
                        encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
 
@@ -186,6 +197,15 @@ get_failed:
                if (greinfo[IFLA_GRE_ERSPAN_INDEX])
                        erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
 
+               if (greinfo[IFLA_GRE_ERSPAN_VER])
+                       erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]);
+
+               if (greinfo[IFLA_GRE_ERSPAN_DIR])
+                       erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]);
+
+               if (greinfo[IFLA_GRE_ERSPAN_HWID])
+                       erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]);
+
                free(answer);
        }
 
@@ -316,6 +336,8 @@ get_failed:
                        encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
                } else if (strcmp(*argv, "noencap-remcsum") == 0) {
                        encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
+               } else if (strcmp(*argv, "external") == 0) {
+                       metadata = 1;
                } else if (strcmp(*argv, "fwmark") == 0) {
                        NEXT_ARG();
                        if (strcmp(*argv, "inherit") == 0) {
@@ -344,31 +366,59 @@ get_failed:
                                invarg("invalid erspan index\n", *argv);
                        if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
                                invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
+               } else if (strcmp(*argv, "erspan_ver") == 0) {
+                       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);
+               } else if (strcmp(*argv, "erspan_dir") == 0) {
+                       NEXT_ARG();
+                       if (matches(*argv, "ingress") == 0)
+                               erspan_dir = 0;
+                       else if (matches(*argv, "egress") == 0)
+                               erspan_dir = 1;
+                       else
+                               invarg("Invalid erspan direction.", *argv);
+               } else if (strcmp(*argv, "erspan_hwid") == 0) {
+                       NEXT_ARG();
+                       if (get_u16(&erspan_hwid, *argv, 0))
+                               invarg("invalid erspan hwid\n", *argv);
                } else
                        usage();
                argc--; argv++;
        }
 
-       addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
-       addattr32(n, 1024, IFLA_GRE_OKEY, okey);
-       addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
-       addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
-       addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
-       addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
-       if (link)
-               addattr32(n, 1024, IFLA_GRE_LINK, link);
-       addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
-       addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
-       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_idx != 0)
-               addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
-
-       addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
-       addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
-       addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
-       addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
+       if (!metadata) {
+               addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
+               addattr32(n, 1024, IFLA_GRE_OKEY, okey);
+               addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
+               addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
+               addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
+               addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
+               if (link)
+                       addattr32(n, 1024, IFLA_GRE_LINK, link);
+               addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
+               addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
+               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) {
+                       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);
+                       } else {
+                               addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
+                               addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
+                       }
+               }
+               addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
+               addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
+               addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
+               addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
+       } else {
+               addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
+       }
 
        return 0;
 }
@@ -388,6 +438,11 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
        if (!tb)
                return;
 
+       if (tb[IFLA_GRE_COLLECT_METADATA]) {
+               print_bool(PRINT_ANY, "collect_metadata", "external", true);
+               return;
+       }
+
        if (tb[IFLA_GRE_FLAGS])
                flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
 
@@ -514,11 +569,33 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 
        if (tb[IFLA_GRE_ERSPAN_INDEX]) {
                __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
-
                print_uint(PRINT_ANY,
                           "erspan_index", "erspan_index %u ", erspan_idx);
        }
 
+       if (tb[IFLA_GRE_ERSPAN_VER]) {
+               __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
+
+               print_uint(PRINT_ANY, "erspan_ver", "erspan_ver %u ", erspan_ver);
+       }
+
+       if (tb[IFLA_GRE_ERSPAN_DIR]) {
+               __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
+
+               if (erspan_dir == 0)
+                       print_string(PRINT_ANY, "erspan_dir",
+                                    "erspan_dir ingress ", NULL);
+               else
+                       print_string(PRINT_ANY, "erspan_dir",
+                                    "erspan_dir egress ", NULL);
+       }
+
+       if (tb[IFLA_GRE_ERSPAN_HWID]) {
+               __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
+
+               print_hex(PRINT_ANY, "erspan_hwid", "erspan_hwid 0x%x ", erspan_hwid);
+       }
+
        tnl_print_encap(tb,
                        IFLA_GRE_ENCAP_TYPE,
                        IFLA_GRE_ENCAP_FLAGS,
index d32f1b8081809a38c3489726cff064fc340b9393..2db151e4dd3c4cc23a09a8e1c90c960337b9b77f 100644 (file)
--- a/lib/bpf.c
+++ b/lib/bpf.c
@@ -1208,7 +1208,7 @@ static int bpf_log_realloc(struct bpf_elf_ctx *ctx)
 
 static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
                          uint32_t size_value, uint32_t max_elem,
-                         uint32_t flags, int inner_fd)
+                         uint32_t flags, int inner_fd, uint32_t ifindex)
 {
        union bpf_attr attr = {};
 
@@ -1218,6 +1218,7 @@ static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
        attr.max_entries = max_elem;
        attr.map_flags = flags;
        attr.inner_map_fd = inner_fd;
+       attr.map_ifindex = ifindex;
 
        return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
 }
@@ -1632,7 +1633,9 @@ static int bpf_map_attach(const char *name, struct bpf_elf_ctx *ctx,
 
        errno = 0;
        fd = bpf_map_create(map->type, map->size_key, map->size_value,
-                           map->max_elem, map->flags, map_inner_fd);
+                           map->max_elem, map->flags, map_inner_fd,
+                           ctx->ifindex);
+
        if (fd < 0 || ctx->verbose) {
                bpf_map_report(fd, name, map, ctx, map_inner_fd);
                if (fd < 0)
index 00e6ce0c8f084fe382970b0ceec73526cd0219a4..7ca47b22581aa52a78e829c48b81672c923b1c12 100644 (file)
@@ -581,30 +581,30 @@ static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err,
                strerror(-err->error));
 }
 
-static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
-                      struct nlmsghdr **answer,
-                      bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+
+static int __rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iov,
+                          size_t iovlen, struct nlmsghdr **answer,
+                          bool show_rtnl_err, nl_ext_ack_fn_t errfn)
 {
-       int status;
-       unsigned int seq;
-       struct nlmsghdr *h;
        struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-       struct iovec iov = {
-               .iov_base = n,
-               .iov_len = n->nlmsg_len
-       };
+       struct iovec riov;
        struct msghdr msg = {
                .msg_name = &nladdr,
                .msg_namelen = sizeof(nladdr),
-               .msg_iov = &iov,
-               .msg_iovlen = 1,
+               .msg_iov = iov,
+               .msg_iovlen = iovlen,
        };
+       unsigned int seq = 0;
+       struct nlmsghdr *h;
+       int i, status;
        char *buf;
 
-       n->nlmsg_seq = seq = ++rtnl->seq;
-
-       if (answer == NULL)
-               n->nlmsg_flags |= NLM_F_ACK;
+       for (i = 0; i < iovlen; i++) {
+               h = iov[i].iov_base;
+               h->nlmsg_seq = seq = ++rtnl->seq;
+               if (answer == NULL)
+                       h->nlmsg_flags |= NLM_F_ACK;
+       }
 
        status = sendmsg(rtnl->fd, &msg, 0);
        if (status < 0) {
@@ -612,8 +612,14 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
                return -1;
        }
 
+       /* change msg to use the response iov */
+       msg.msg_iov = &riov;
+       msg.msg_iovlen = 1;
+       i = 0;
        while (1) {
+next:
                status = rtnl_recvmsg(rtnl->fd, &msg, &buf);
+               ++i;
 
                if (status < 0)
                        return status;
@@ -642,7 +648,7 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
 
                        if (nladdr.nl_pid != 0 ||
                            h->nlmsg_pid != rtnl->local.nl_pid ||
-                           h->nlmsg_seq != seq) {
+                           h->nlmsg_seq > seq || h->nlmsg_seq < seq - iovlen) {
                                /* Don't forget to skip that message. */
                                status -= NLMSG_ALIGN(len);
                                h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
@@ -662,7 +668,10 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
                                                *answer = (struct nlmsghdr *)buf;
                                        else
                                                free(buf);
-                                       return 0;
+                                       if (h->nlmsg_seq == seq)
+                                               return 0;
+                                       else
+                                               goto next;
                                }
 
                                if (rtnl->proto != NETLINK_SOCK_DIAG &&
@@ -671,7 +680,7 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
 
                                errno = -err->error;
                                free(buf);
-                               return -1;
+                               return -i;
                        }
 
                        if (answer) {
@@ -698,12 +707,30 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
        }
 }
 
+static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+                      struct nlmsghdr **answer,
+                      bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+{
+       struct iovec iov = {
+               .iov_base = n,
+               .iov_len = n->nlmsg_len
+       };
+
+       return __rtnl_talk_iov(rtnl, &iov, 1, answer, show_rtnl_err, errfn);
+}
+
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
              struct nlmsghdr **answer)
 {
        return __rtnl_talk(rtnl, n, answer, true, NULL);
 }
 
+int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen,
+                 struct nlmsghdr **answer)
+{
+       return __rtnl_talk_iov(rtnl, iovec, iovlen, answer, true, NULL);
+}
+
 int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
                     struct nlmsghdr **answer,
                     nl_ext_ack_fn_t errfn)
index 9fa522047fdcabbf3166b16bf4053534c228deb4..e20b60e4bcbf870cdae576d1afe8f20b11e4c909 100644 (file)
@@ -534,7 +534,7 @@ int get_addr64(__u64 *ap, const char *cp)
        return 1;
 }
 
-int get_addr_1(inet_prefix *addr, const char *name, int family)
+static int __get_addr_1(inet_prefix *addr, const char *name, int family)
 {
        memset(addr, 0, sizeof(*addr));
 
@@ -543,9 +543,9 @@ int get_addr_1(inet_prefix *addr, const char *name, int family)
            strcmp(name, "any") == 0) {
                if ((family == AF_DECnet) || (family == AF_MPLS))
                        return -1;
-               addr->family = family;
-               addr->bytelen = (family == AF_INET6 ? 16 : 4);
-               addr->bitlen = -1;
+               addr->family = (family != AF_UNSPEC) ? family : AF_INET;
+               addr->bytelen = af_byte_len(addr->family);
+               addr->bitlen = -2;
                return 0;
        }
 
@@ -619,6 +619,36 @@ int get_addr_1(inet_prefix *addr, const char *name, int family)
        return 0;
 }
 
+int get_addr_1(inet_prefix *addr, const char *name, int family)
+{
+       int ret;
+
+       ret = __get_addr_1(addr, name, family);
+       if (ret)
+               return ret;
+
+       switch (addr->family) {
+       case AF_INET:
+               if (!addr->data[0])
+                       addr->flags |= ADDRTYPE_INET_UNSPEC;
+               else if (IN_MULTICAST(ntohl(addr->data[0])))
+                       addr->flags |= ADDRTYPE_INET_MULTI;
+               else
+                       addr->flags |= ADDRTYPE_INET;
+               break;
+       case AF_INET6:
+               if (IN6_IS_ADDR_UNSPECIFIED(addr->data))
+                       addr->flags |= ADDRTYPE_INET_UNSPEC;
+               else if (IN6_IS_ADDR_MULTICAST(addr->data))
+                       addr->flags |= ADDRTYPE_INET_MULTI;
+               else
+                       addr->flags |= ADDRTYPE_INET;
+               break;
+       }
+
+       return 0;
+}
+
 int af_bit_len(int af)
 {
        switch (af) {
@@ -644,46 +674,46 @@ int af_byte_len(int af)
 
 int get_prefix_1(inet_prefix *dst, char *arg, int family)
 {
-       int err;
-       unsigned int plen;
        char *slash;
-
-       memset(dst, 0, sizeof(*dst));
-
-       if (strcmp(arg, "default") == 0 ||
-           strcmp(arg, "any") == 0 ||
-           strcmp(arg, "all") == 0) {
-               if ((family == AF_DECnet) || (family == AF_MPLS))
-                       return -1;
-               dst->family = family;
-               dst->bytelen = 0;
-               dst->bitlen = 0;
-               dst->flags |= PREFIXLEN_SPECIFIED;
-               return 0;
-       }
+       int err, bitlen, flags;
 
        slash = strchr(arg, '/');
        if (slash)
                *slash = 0;
 
        err = get_addr_1(dst, arg, family);
-       if (err == 0) {
-               dst->bitlen = af_bit_len(dst->family);
-
-               if (slash) {
-                       if (get_netmask(&plen, slash+1, 0)
-                           || plen > dst->bitlen) {
-                               err = -1;
-                               goto done;
-                       }
-                       dst->flags |= PREFIXLEN_SPECIFIED;
-                       dst->bitlen = plen;
-               }
-       }
-done:
+
        if (slash)
                *slash = '/';
-       return err;
+
+       if (err)
+               return err;
+
+       bitlen = af_bit_len(dst->family);
+
+       flags = PREFIXLEN_SPECIFIED;
+       if (slash) {
+               unsigned int plen;
+
+               if (dst->bitlen == -2)
+                       return -1;
+               if (get_netmask(&plen, slash + 1, 0))
+                       return -1;
+               if (plen > bitlen)
+                       return -1;
+
+               bitlen = plen;
+       } else {
+               if (dst->bitlen == -2)
+                       bitlen = 0;
+               else
+                       flags = 0;
+       }
+
+       dst->flags |= flags;
+       dst->bitlen = bitlen;
+
+       return 0;
 }
 
 static const char *family_name_verbose(int family)
@@ -1250,14 +1280,6 @@ int makeargs(char *line, char *argv[], int maxargs)
        return argc;
 }
 
-int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6)
-{
-       if (strchr(src, ':'))
-               return inet_pton(AF_INET6, src, dst6);
-       else
-               return inet_pton(AF_INET, src, dst);
-}
-
 void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n)
 {
        char *tstr;
index 5e49850258fad93eee82dd2019b923bfb6422d9d..481589ea368ad7e25dbaaa74cdf1b471289cb205 100644 (file)
@@ -36,6 +36,11 @@ ip-link \- network device configuration
 .RB "[ " numrxqueues
 .IR QUEUE_COUNT " ]"
 .br
+.BR "[ " gso_max_size
+.IR BYTES " ]"
+.RB "[ " gso_max_segs
+.IR SEGMENTS " ]"
+.br
 .BI type " TYPE"
 .RI "[ " ARGS " ]"
 
@@ -213,7 +218,8 @@ ip-link \- network device configuration
 .BR lowpan " |"
 .BR geneve " |"
 .BR vrf " |"
-.BR macsec " ]"
+.BR macsec " |"
+.BR netdevsim " ]"
 
 .ti -8
 .IR ETYPE " := [ " TYPE " |"
@@ -333,6 +339,9 @@ Link types:
 .sp
 .BR vrf
 - Interface for L3 VRF domains
+.sp
+.BR netdevsim
+- Interface for netdev API tests
 .in -8
 
 .TP
@@ -343,6 +352,14 @@ specifies the number of transmit queues for new device.
 .BI numrxqueues " QUEUE_COUNT "
 specifies the number of receive queues for new device.
 
+.TP
+.BI gso_max_size " BYTES "
+specifies the recommended maximum size of a Generic Segment Offload packet the new device should accept.
+
+.TP
+.BI gso_max_segs " SEGMENTS "
+specifies the recommended maximum number of a Generic Segment Offload segments the new device should accept.
+
 .TP
 .BI index " IDX "
 specifies the desired index of the new virtual device. The link creation fails, if the index is busy.
@@ -676,13 +693,13 @@ tunnel.
 .in -8
 
 .TP
-GRE, IPIP, SIT, ERSPAN Type Support
+GRE, IPIP, SIT Type Support
 For a link of types
-.I GRE/IPIP/SIT/ERSPAN
+.I GRE/IPIP/SIT
 the following additional arguments are supported:
 
 .BI "ip link add " DEVICE
-.BR type " { " gre " | " ipip " | " sit " | " erspan " }"
+.BR type " { " gre " | " ipip " | " sit " }"
 .BI " remote " ADDR " local " ADDR
 [
 .BR encap " { " fou " | " gue " | " none " }"
@@ -697,8 +714,6 @@ the following additional arguments are supported:
 ] [
 .I " mode " { ip6ip | ipip | mplsip | any } "
 ] [
-.BR erspan " \fIIDX "
-] [
 .BR external
 ]
 
@@ -744,13 +759,6 @@ MPLS-Over-IPv4, "any" indicates IPv6, IPv4 or MPLS Over IPv4. Supported for
 SIT where the default is "ip6ip" and IPIP where the default is "ipip".
 IPv6-Over-IPv4 is not supported for IPIP.
 
-.sp
-.BR erspan " \fIIDX "
-- specifies the ERSPAN index field.
-.IR IDX
-indicates a 20 bit index/port number associated with the ERSPAN
-traffic's source port and direction.
-
 .sp
 .BR external
 - make this tunnel externally controlled
@@ -786,6 +794,8 @@ the following additional arguments are supported:
 .BI "dscp inherit"
 ] [
 .BI dev " PHYS_DEV "
+] [
+.RB external
 ]
 
 .in +8
@@ -864,6 +874,21 @@ or
 .IR 00 ".." ff
 when tunneling non-IP packets. The default value is 00.
 
+.sp
+.RB external
+- make this tunnel externally controlled (or not, which is the default).
+In the kernel, this is referred to as collect metadata mode.  This flag is
+mutually exclusive with the
+.BR remote ,
+.BR local ,
+.BR seq ,
+.BR key,
+.BR csum,
+.BR hoplimit,
+.BR encaplimit,
+.BR flowlabel " and " tclass
+options.
+
 .in -8
 
 .TP
@@ -883,6 +908,76 @@ the following additional arguments are supported:
 .BI  mode " MODE "
 - specifies the mode (datagram or connected) to use.
 
+.TP
+ERSPAN Type Support
+For a link of type
+.I ERSPAN/IP6ERSPAN
+the following additional arguments are supported:
+
+.BI "ip link add " DEVICE
+.BR type " { " erspan " | " ip6erspan " }"
+.BI remote " ADDR " local " ADDR " seq
+.RB key
+.I KEY
+.BR erspan_ver " \fIversion "
+[
+.BR erspan " \fIIDX "
+] [
+.BR erspan_dir " { " \fIingress " | " \fIegress " }"
+] [
+.BR erspan_hwid " \fIhwid "
+] [
+.RB external
+]
+
+.in +8
+.sp
+.BI  remote " ADDR "
+- specifies the remote address of the tunnel.
+
+.sp
+.BI  local " ADDR "
+- specifies the fixed local address for tunneled packets.
+It must be an address on another interface on this host.
+
+.sp
+.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).
+
+.sp
+.BR erspan " \fIIDX "
+- specifies the ERSPAN v1 index field.
+.IR IDX
+indicates a 20 bit index/port number associated with the ERSPAN
+traffic's source port and direction.
+
+.sp
+.BR erspan_dir " { " \fIingress " | " \fIegress " }"
+- specifies the ERSPAN v2 mirrored traffic's direction.
+
+.sp
+.BR erspan_hwid " \fIhwid "
+- an unique identifier of an ERSPAN v2 engine within a system.
+.IR hwid
+is a 6-bit value for users to configure.
+
+.sp
+.BR external
+- make this tunnel externally controlled (or not, which is the default).
+In the kernel, this is referred to as collect metadata mode.  This flag is
+mutually exclusive with the
+.BR remote ,
+.BR local ,
+.BR erspan_ver ,
+.BR erspan ,
+.BR erspan_dir " and " erspan_hwid
+options.
+
+.in -8
+
 .TP
 GENEVE Type Support
 For a link of type
@@ -2065,6 +2160,13 @@ ip link add link wpan0 lowpan0 type lowpan
 Creates a 6LoWPAN interface named lowpan0 on the underlying
 IEEE 802.15.4 device wpan0.
 .RE
+.PP
+ip link add dev ip6erspan11 type ip6erspan seq key 102
+local fc00:100::2 remote fc00:100::1
+erspan_ver 2 erspan_dir ingress erspan_hwid 17
+.RS 4
+Creates a IP6ERSPAN version 2 interface named ip6erspan00.
+.RE
 
 .SH SEE ALSO
 .br
index ff071b337b3c5d0471f58a71389fef6c2c46ee1c..5ffea373d18b713dfa701650b3dcbeae3db295d7 100644 (file)
@@ -11,7 +11,11 @@ tc \- show / manipulate traffic control settings
 \fIqdisc-id\fR
 .B | root ]
 .B [ handle
-\fIqdisc-id\fR ] qdisc
+\fIqdisc-id\fR ]
+.B [ ingress_block
+\fIBLOCK_INDEX\fR ]
+.B [ egress_block
+\fIBLOCK_INDEX\fR ] qdisc
 [ qdisc specific parameters ]
 .P
 
@@ -41,6 +45,19 @@ tc \- show / manipulate traffic control settings
 .B flowid
 \fIflow-id\fR
 
+.B tc
+.RI "[ " OPTIONS " ]"
+.B filter [ add | change | replace | delete | get ] block
+\fIBLOCK_INDEX\fR
+.B [ handle \fIfilter-id\fR ]
+.B protocol
+\fIprotocol\fR
+.B prio
+\fIpriority\fR filtertype
+[ filtertype specific parameters ]
+.B flowid
+\fIflow-id\fR
+
 .B tc
 .RI "[ " OPTIONS " ]"
 .RI "[ " FORMAT " ]"
@@ -58,6 +75,11 @@ tc \- show / manipulate traffic control settings
 .RI "[ " OPTIONS " ]"
 .B filter show dev
 \fIDEV\fR
+.P
+.B tc
+.RI "[ " OPTIONS " ]"
+.B filter show block
+\fIBLOCK_INDEX\fR
 
 .P
 .ti 8
index 9fadf3ac3b9cd45388d962b35dc99694c551c642..03ab8683332d661c9cc69faa75e7722782fd892a 100644 (file)
@@ -241,33 +241,7 @@ static int dev_one_show(struct rd *rd)
 
 static int dev_show(struct rd *rd)
 {
-       struct dev_map *dev_map;
-       int ret = 0;
-
-       if (rd->json_output)
-               jsonw_start_array(rd->jw);
-       if (rd_no_arg(rd)) {
-               list_for_each_entry(dev_map, &rd->dev_map_list, list) {
-                       rd->dev_idx = dev_map->idx;
-                       ret = dev_one_show(rd);
-                       if (ret)
-                               goto out;
-               }
-       } else {
-               dev_map = dev_map_lookup(rd, false);
-               if (!dev_map) {
-                       pr_err("Wrong device name\n");
-                       ret = -ENOENT;
-                       goto out;
-               }
-               rd_arg_inc(rd);
-               rd->dev_idx = dev_map->idx;
-               ret = dev_one_show(rd);
-       }
-out:
-       if (rd->json_output)
-               jsonw_end_array(rd->jw);
-       return ret;
+       return rd_exec_dev(rd, dev_one_show);
 }
 
 int cmd_dev(struct rd *rd)
index 3a4b00bd533b18267fdfe203e0fea8494236539c..676cb21d216716f61b861c91764d89114589aac7 100644 (file)
@@ -30,7 +30,7 @@ static const char *caps_to_str(uint32_t idx)
        x(PKEY_NVRAM, 8) \
        x(LED_INFO, 9) \
        x(SM_DISABLED, 10) \
-       x(SYS_IMAGE_GUIG, 11) \
+       x(SYS_IMAGE_GUID, 11) \
        x(PKEY_SW_EXT_PORT_TRAP, 12) \
        x(EXTENDED_SPEEDS, 14) \
        x(CM, 16) \
@@ -277,56 +277,15 @@ static int link_one_show(struct rd *rd)
                { 0 }
        };
 
+       if (!rd->port_idx)
+               return 0;
+
        return rd_exec_cmd(rd, cmds, "parameter");
 }
 
 static int link_show(struct rd *rd)
 {
-       struct dev_map *dev_map;
-       uint32_t port;
-       int ret = 0;
-
-       if (rd->json_output)
-               jsonw_start_array(rd->jw);
-       if (rd_no_arg(rd)) {
-               list_for_each_entry(dev_map, &rd->dev_map_list, list) {
-                       rd->dev_idx = dev_map->idx;
-                       for (port = 1; port < dev_map->num_ports + 1; port++) {
-                               rd->port_idx = port;
-                               ret = link_one_show(rd);
-                               if (ret)
-                                       goto out;
-                       }
-               }
-
-       } else {
-               dev_map = dev_map_lookup(rd, true);
-               port = get_port_from_argv(rd);
-               if (!dev_map || port > dev_map->num_ports) {
-                       pr_err("Wrong device name\n");
-                       ret = -ENOENT;
-                       goto out;
-               }
-               rd_arg_inc(rd);
-               rd->dev_idx = dev_map->idx;
-               rd->port_idx = port ? : 1;
-               for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) {
-                       ret = link_one_show(rd);
-                       if (ret)
-                               goto out;
-                       if (port)
-                               /*
-                                * We got request to show link for devname
-                                * with port index.
-                                */
-                               break;
-               }
-       }
-
-out:
-       if (rd->json_output)
-               jsonw_end_array(rd->jw);
-       return ret;
+       return rd_exec_link(rd, link_one_show);
 }
 
 int cmd_link(struct rd *rd)
index f9f4f2a2ad76ddb46113f5788b55bcaa9ae26ad0..0e8fd688674ce8e3622af6546d8b1286ec878346 100644 (file)
@@ -70,12 +70,11 @@ static int rd_init(struct rd *rd, int argc, char **argv, char *filename)
        return rd_recv_msg(rd, rd_dev_init_cb, rd, seq);
 }
 
-static void rd_free(struct rd *rd)
+static void rd_cleanup(struct rd *rd)
 {
        if (rd->json_output)
                jsonw_destroy(&rd->jw);
-       free(rd->buff);
-       rd_free_devmap(rd);
+       rd_free(rd);
 }
 
 int main(int argc, char **argv)
@@ -138,6 +137,6 @@ int main(int argc, char **argv)
        err = rd_cmd(&rd);
 out:
        /* Always cleanup */
-       rd_free(&rd);
+       rd_cleanup(&rd);
        return err ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index d551eb2966e11d9810edaf0c2e7f9dab7af487e0..8d53d3a03b91fac87e2e9f7d0b382d83c557d25c 100644 (file)
@@ -72,13 +72,14 @@ uint32_t get_port_from_argv(struct rd *rd);
 int cmd_dev(struct rd *rd);
 int cmd_link(struct rd *rd);
 int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str);
+int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd));
+int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd));
+void rd_free(struct rd *rd);
 
 /*
  * Device manipulation
  */
-void rd_free_devmap(struct rd *rd);
 struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index);
-struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name);
 
 /*
  * Netlink
index eb4377cf2e185d19a6f88d7487c71043ccd18ffa..7b2001e2091d3b5eb5c075b978d927ada4c7a6b3 100644 (file)
@@ -71,15 +71,6 @@ static struct dev_map *dev_map_alloc(const char *dev_name)
        return dev_map;
 }
 
-static void dev_map_free(struct dev_map *dev_map)
-{
-       if (!dev_map)
-               return;
-
-       free(dev_map->dev_name);
-       free(dev_map);
-}
-
 static void dev_map_cleanup(struct rd *rd)
 {
        struct dev_map *dev_map, *tmp;
@@ -87,7 +78,8 @@ static void dev_map_cleanup(struct rd *rd)
        list_for_each_entry_safe(dev_map, tmp,
                                 &rd->dev_map_list, list) {
                list_del(&dev_map->list);
-               dev_map_free(dev_map);
+               free(dev_map->dev_name);
+               free(dev_map);
        }
 }
 
@@ -152,13 +144,94 @@ int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data)
        return MNL_CB_OK;
 }
 
-void rd_free_devmap(struct rd *rd)
+void rd_free(struct rd *rd)
 {
        if (!rd)
                return;
+       free(rd->buff);
        dev_map_cleanup(rd);
 }
 
+int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd))
+{
+       struct dev_map *dev_map;
+       uint32_t port;
+       int ret = 0;
+
+       if (rd->json_output)
+               jsonw_start_array(rd->jw);
+       if (rd_no_arg(rd)) {
+               list_for_each_entry(dev_map, &rd->dev_map_list, list) {
+                       rd->dev_idx = dev_map->idx;
+                       for (port = 1; port < dev_map->num_ports + 1; port++) {
+                               rd->port_idx = port;
+                               ret = cb(rd);
+                               if (ret)
+                                       goto out;
+                       }
+               }
+
+       } else {
+               dev_map = dev_map_lookup(rd, true);
+               port = get_port_from_argv(rd);
+               if (!dev_map || port > dev_map->num_ports) {
+                       pr_err("Wrong device name\n");
+                       ret = -ENOENT;
+                       goto out;
+               }
+               rd_arg_inc(rd);
+               rd->dev_idx = dev_map->idx;
+               rd->port_idx = port ? : 1;
+               for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) {
+                       ret = cb(rd);
+                       if (ret)
+                               goto out;
+                       if (port)
+                               /*
+                                * We got request to show link for devname
+                                * with port index.
+                                */
+                               break;
+               }
+       }
+
+out:
+       if (rd->json_output)
+               jsonw_end_array(rd->jw);
+       return ret;
+}
+
+int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd))
+{
+       struct dev_map *dev_map;
+       int ret = 0;
+
+       if (rd->json_output)
+               jsonw_start_array(rd->jw);
+       if (rd_no_arg(rd)) {
+               list_for_each_entry(dev_map, &rd->dev_map_list, list) {
+                       rd->dev_idx = dev_map->idx;
+                       ret = cb(rd);
+                       if (ret)
+                               goto out;
+               }
+       } else {
+               dev_map = dev_map_lookup(rd, false);
+               if (!dev_map) {
+                       pr_err("Wrong device name - %s\n", rd_argv(rd));
+                       ret = -ENOENT;
+                       goto out;
+               }
+               rd_arg_inc(rd);
+               rd->dev_idx = dev_map->idx;
+               ret = cb(rd);
+       }
+out:
+       if (rd->json_output)
+               jsonw_end_array(rd->jw);
+       return ret;
+}
+
 int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str)
 {
        const struct rd_cmd *c;
@@ -236,7 +309,7 @@ int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq)
        return ret;
 }
 
-struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name)
+static struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name)
 {
        struct dev_map *dev_map;
 
@@ -253,6 +326,9 @@ struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index)
        char *dev_name;
        char *slash;
 
+       if (rd_no_arg(rd))
+               return NULL;
+
        dev_name = strdup(rd_argv(rd));
        if (allow_port_index) {
                slash = strrchr(dev_name, '/');
index d3df93c066a891842e6018dcb6c179e5abecaf33..445d0b69bb7566586830dbe9e0dc765dbfeb21c2 100644 (file)
@@ -549,40 +549,61 @@ bad_val:
        return ret;
 }
 
+struct tc_action_req {
+       struct nlmsghdr         n;
+       struct tcamsg           t;
+       char                    buf[MAX_MSG];
+};
+
 static int tc_action_modify(int cmd, unsigned int flags,
-                           int *argc_p, char ***argv_p)
+                           int *argc_p, char ***argv_p,
+                           void *buf, size_t buflen)
 {
-       int argc = *argc_p;
+       struct tc_action_req *req, action_req;
        char **argv = *argv_p;
+       struct rtattr *tail;
+       int argc = *argc_p;
+       struct iovec iov;
        int ret = 0;
-       struct {
-               struct nlmsghdr         n;
-               struct tcamsg           t;
-               char                    buf[MAX_MSG];
-       } req = {
-               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
-               .n.nlmsg_flags = NLM_F_REQUEST | flags,
-               .n.nlmsg_type = cmd,
-               .t.tca_family = AF_UNSPEC,
-       };
-       struct rtattr *tail = NLMSG_TAIL(&req.n);
+
+       if (buf) {
+               req = buf;
+               if (buflen < sizeof (struct tc_action_req)) {
+                       fprintf(stderr, "buffer is too small: %zu\n", buflen);
+                       return -1;
+               }
+       } else {
+               memset(&action_req, 0, sizeof (struct tc_action_req));
+               req = &action_req;
+       }
+
+       req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
+       req->n.nlmsg_flags = NLM_F_REQUEST | flags;
+       req->n.nlmsg_type = cmd;
+       req->t.tca_family = AF_UNSPEC;
+       tail = NLMSG_TAIL(&req->n);
 
        argc -= 1;
        argv += 1;
-       if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
+       if (parse_action(&argc, &argv, TCA_ACT_TAB, &req->n)) {
                fprintf(stderr, "Illegal \"action\"\n");
                return -1;
        }
-       tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+       tail->rta_len = (void *) NLMSG_TAIL(&req->n) - (void *) tail;
+
+       *argc_p = argc;
+       *argv_p = argv;
+
+       if (buf)
+               return 0;
 
-       if (rtnl_talk(&rth, &req.n, NULL) < 0) {
+       iov.iov_base = &req->n;
+       iov.iov_len = req->n.nlmsg_len;
+       if (rtnl_talk_iov(&rth, &iov, 1, NULL) < 0) {
                fprintf(stderr, "We have an error talking to the kernel\n");
                ret = -1;
        }
 
-       *argc_p = argc;
-       *argv_p = argv;
-
        return ret;
 }
 
@@ -682,7 +703,7 @@ bad_val:
        return ret;
 }
 
-int do_action(int argc, char **argv)
+int do_action(int argc, char **argv, void *buf, size_t buflen)
 {
 
        int ret = 0;
@@ -692,12 +713,12 @@ int do_action(int argc, char **argv)
                if (matches(*argv, "add") == 0) {
                        ret =  tc_action_modify(RTM_NEWACTION,
                                                NLM_F_EXCL | NLM_F_CREATE,
-                                               &argc, &argv);
+                                               &argc, &argv, buf, buflen);
                } else if (matches(*argv, "change") == 0 ||
                          matches(*argv, "replace") == 0) {
                        ret = tc_action_modify(RTM_NEWACTION,
                                               NLM_F_CREATE | NLM_F_REPLACE,
-                                              &argc, &argv);
+                                              &argc, &argv, buf, buflen);
                } else if (matches(*argv, "delete") == 0) {
                        argc -= 1;
                        argv += 1;
diff --git a/tc/tc.c b/tc/tc.c
index ad9f07e9786de0e93c1d2b66d194b6a6c44a89bb..63e64fece87dc8b950b3784060e444f344d05053 100644 (file)
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -193,16 +193,16 @@ static void usage(void)
                        "                    -nm | -nam[es] | { -cf | -conf } path } | -j[son]\n");
 }
 
-static int do_cmd(int argc, char **argv)
+static int do_cmd(int argc, char **argv, void *buf, size_t buflen)
 {
        if (matches(*argv, "qdisc") == 0)
                return do_qdisc(argc-1, argv+1);
        if (matches(*argv, "class") == 0)
                return do_class(argc-1, argv+1);
        if (matches(*argv, "filter") == 0)
-               return do_filter(argc-1, argv+1);
+               return do_filter(argc-1, argv+1, buf, buflen);
        if (matches(*argv, "actions") == 0)
-               return do_action(argc-1, argv+1);
+               return do_action(argc-1, argv+1, buf, buflen);
        if (matches(*argv, "monitor") == 0)
                return do_tcmonitor(argc-1, argv+1);
        if (matches(*argv, "exec") == 0)
@@ -217,11 +217,110 @@ static int do_cmd(int argc, char **argv)
        return -1;
 }
 
+#define TC_MAX_SUBC    10
+static bool batchsize_enabled(int argc, char *argv[])
+{
+       struct {
+               char *c;
+               char *subc[TC_MAX_SUBC];
+       } table[] = {
+               {"filter", {"add", "delete", "change", "replace", NULL}},
+               {"actions", {"add", "change", "replace", NULL}},
+               { NULL },
+       }, *iter;
+       char *s;
+       int i;
+
+       if (argc < 2)
+               return false;
+
+       for (iter = table; iter->c; iter++) {
+               if (matches(argv[0], iter->c))
+                       continue;
+               for (i = 0; i < TC_MAX_SUBC; i++) {
+                       s = iter->subc[i];
+                       if (s && matches(argv[1], s) == 0)
+                               return true;
+               }
+       }
+
+       return false;
+}
+
+struct batch_buf {
+       struct batch_buf        *next;
+       char                    buf[16420];     /* sizeof (struct nlmsghdr) +
+                                                  max(sizeof (struct tcmsg) +
+                                                  sizeof (struct tcamsg)) +
+                                                  MAX_MSG */
+};
+
+static struct batch_buf *get_batch_buf(struct batch_buf **pool,
+                                      struct batch_buf **head,
+                                      struct batch_buf **tail)
+{
+       struct batch_buf *buf;
+
+       if (*pool == NULL)
+               buf = calloc(1, sizeof (struct batch_buf));
+       else {
+               buf = *pool;
+               *pool = (*pool)->next;
+               memset(buf, 0, sizeof (struct batch_buf));
+       }
+
+       if (*head == NULL)
+               *head = *tail = buf;
+       else {
+               (*tail)->next = buf;
+               (*tail) = buf;
+       }
+
+       return buf;
+}
+
+static void put_batch_bufs(struct batch_buf **pool,
+                          struct batch_buf **head,
+                          struct batch_buf **tail)
+{
+       if (*head == NULL || *tail == NULL)
+               return;
+
+       if (*pool == NULL)
+               *pool = *head;
+       else {
+               (*tail)->next = *pool;
+               *pool = *head;
+       }
+       *head = NULL;
+       *tail = NULL;
+}
+
+static void free_batch_bufs(struct batch_buf **pool)
+{
+       struct batch_buf *buf;
+
+       for (buf = *pool; buf != NULL; buf = *pool) {
+               *pool = buf->next;
+               free(buf);
+       }
+       *pool = NULL;
+}
+
 static int batch(const char *name)
 {
-       char *line = NULL;
+       struct batch_buf *head = NULL, *tail = NULL, *buf_pool = NULL;
+       char *largv[100], *largv_next[100];
+       char *line, *line_next = NULL;
+       bool bs_enabled_next = false;
+       bool bs_enabled = false;
+       bool lastline = false;
+       int largc, largc_next;
+       bool bs_enabled_saved;
+       int batchsize = 0;
        size_t len = 0;
        int ret = 0;
+       bool send;
 
        batch_mode = 1;
        if (name && strcmp(name, "-") != 0) {
@@ -240,25 +339,95 @@ static int batch(const char *name)
        }
 
        cmdlineno = 0;
-       while (getcmdline(&line, &len, stdin) != -1) {
-               char *largv[100];
-               int largc;
+       if (getcmdline(&line, &len, stdin) == -1)
+               goto Exit;
+       largc = makeargs(line, largv, 100);
+       bs_enabled = batchsize_enabled(largc, largv);
+       bs_enabled_saved = bs_enabled;
+       do {
+               if (getcmdline(&line_next, &len, stdin) == -1)
+                       lastline = true;
+
+               largc_next = makeargs(line_next, largv_next, 100);
+               bs_enabled_next = batchsize_enabled(largc_next, largv_next);
+               if (bs_enabled) {
+                       struct batch_buf *buf;
+
+                       buf = get_batch_buf(&buf_pool, &head, &tail);
+                       if (!buf) {
+                               fprintf(stderr,
+                                       "failed to allocate batch_buf\n");
+                               return -1;
+                       }
+                       ++batchsize;
+               }
 
-               largc = makeargs(line, largv, 100);
-               if (largc == 0)
+               /*
+                * In batch mode, if we haven't accumulated enough commands
+                * and this is not the last command and this command & next
+                * command both support the batchsize feature, don't send the
+                * message immediately.
+                */
+               if (!lastline && bs_enabled && bs_enabled_next
+                   && batchsize != MSG_IOV_MAX)
+                       send = false;
+               else
+                       send = true;
+
+               line = line_next;
+               line_next = NULL;
+               len = 0;
+               bs_enabled_saved = bs_enabled;
+               bs_enabled = bs_enabled_next;
+               bs_enabled_next = false;
+
+               if (largc == 0) {
+                       largc = largc_next;
+                       memcpy(largv, largv_next, largc * sizeof(char *));
                        continue;       /* blank line */
+               }
 
-               if (do_cmd(largc, largv)) {
-                       fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno);
+               ret = do_cmd(largc, largv, tail == NULL ? NULL : tail->buf,
+                            tail == NULL ? 0 : sizeof (tail->buf));
+               if (ret != 0) {
+                       fprintf(stderr, "Command failed %s:%d\n", name,
+                               cmdlineno - 1);
                        ret = 1;
                        if (!force)
                                break;
                }
-       }
-       if (line)
-               free(line);
+               largc = largc_next;
+               memcpy(largv, largv_next, largc * sizeof(char *));
+
+               if (send && bs_enabled_saved) {
+                       struct iovec *iov, *iovs;
+                       struct batch_buf *buf;
+                       struct nlmsghdr *n;
+
+                       iov = iovs = malloc(batchsize * sizeof (struct iovec));
+                       for (buf = head; buf != NULL; buf = buf->next, ++iov) {
+                               n = (struct nlmsghdr *)&buf->buf;
+                               iov->iov_base = n;
+                               iov->iov_len = n->nlmsg_len;
+                       }
+
+                       ret = rtnl_talk_iov(&rth, iovs, batchsize, NULL);
+                       if (ret < 0) {
+                               fprintf(stderr, "Command failed %s:%d\n", name,
+                                       cmdlineno - (batchsize + ret) - 1);
+                               return 2;
+                       }
+                       put_batch_bufs(&buf_pool, &head, &tail);
+                       batchsize = 0;
+                       free(iovs);
+               }
+       } while (!lastline);
 
+       free_batch_bufs(&buf_pool);
+Exit:
+       free(line);
        rtnl_close(&rth);
+
        return ret;
 }
 
@@ -341,7 +510,7 @@ int main(int argc, char **argv)
                goto Exit;
        }
 
-       ret = do_cmd(argc-1, argv+1);
+       ret = do_cmd(argc-1, argv+1, NULL, 0);
 Exit:
        rtnl_close(&rth);
 
index 264fbdac0288d096564af2566f25ed3540b8e755..49c24616c2c357b7723b0ff3621156ba903b00cb 100644 (file)
@@ -1,13 +1,14 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
 #define TCA_BUF_MAX    (64*1024)
+#define MSG_IOV_MAX    128
 
 extern struct rtnl_handle rth;
 
 extern int do_qdisc(int argc, char **argv);
 extern int do_class(int argc, char **argv);
-extern int do_filter(int argc, char **argv);
-extern int do_action(int argc, char **argv);
+extern int do_filter(int argc, char **argv, void *buf, size_t buflen);
+extern int do_action(int argc, char **argv, void *buf, size_t buflen);
 extern int do_tcmonitor(int argc, char **argv);
 extern int do_exec(int argc, char **argv);
 
index 546311a8135ccb68658a7b6ec2968f52920d7294..5c31a4cea658b229fb21b6888634b773acb699d0 100644 (file)
 static void usage(void)
 {
        fprintf(stderr,
-               "Usage: tc filter [ add | del | change | replace | show ] dev STRING\n"
-               "Usage: tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
+               "Usage: tc filter [ add | del | change | replace | show ] [ dev STRING ]\n"
+               "       tc filter [ add | del | change | replace | show ] [ block BLOCK_INDEX ]\n"
+               "       tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
+               "       tc filter get block BLOCK_INDEX protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
                "       [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]\n"
                "       [ estimator INTERVAL TIME_CONSTANT ]\n"
                "       [ root | ingress | egress | parent CLASSID ]\n"
                "       [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"
                "\n"
                "       tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n"
+               "       tc filter show [ block BLOCK_INDEX ]\n"
                "Where:\n"
                "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n"
                "FILTERID := ... format depends on classifier, see there\n"
                "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n");
 }
 
-static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
+struct tc_filter_req {
+       struct nlmsghdr         n;
+       struct tcmsg            t;
+       char                    buf[MAX_MSG];
+};
+
+static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
+                           void *buf, size_t buflen)
 {
-       struct {
-               struct nlmsghdr n;
-               struct tcmsg            t;
-               char                    buf[MAX_MSG];
-       } req = {
-               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
-               .n.nlmsg_flags = NLM_F_REQUEST | flags,
-               .n.nlmsg_type = cmd,
-               .t.tcm_family = AF_UNSPEC,
-       };
+       struct tc_filter_req *req, filter_req;
        struct filter_util *q = NULL;
-       __u32 prio = 0;
-       __u32 protocol = 0;
-       int protocol_set = 0;
-       __u32 chain_index;
+       struct tc_estimator est = {};
+       char k[FILTER_NAMESZ] = {};
        int chain_index_set = 0;
+       char d[IFNAMSIZ] = {};
+       int protocol_set = 0;
+       __u32 block_index = 0;
        char *fhandle = NULL;
-       char  d[IFNAMSIZ] = {};
-       char  k[FILTER_NAMESZ] = {};
-       struct tc_estimator est = {};
+       __u32 protocol = 0;
+       __u32 chain_index;
+       struct iovec iov;
+       __u32 prio = 0;
+       int ret;
+
+       if (buf) {
+               req = buf;
+               if (buflen < sizeof (struct tc_filter_req)) {
+                       fprintf(stderr, "buffer is too small: %zu\n", buflen);
+                       return -1;
+               }
+       } else {
+               memset(&filter_req, 0, sizeof (struct tc_filter_req));
+               req = &filter_req;
+       }
+
+       req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+       req->n.nlmsg_flags = NLM_F_REQUEST | flags;
+       req->n.nlmsg_type = cmd;
+       req->t.tcm_family = AF_UNSPEC;
 
        if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
                protocol = htons(ETH_P_ALL);
@@ -73,39 +93,53 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
                        NEXT_ARG();
                        if (d[0])
                                duparg("dev", *argv);
+                       if (block_index) {
+                               fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
+                               return -1;
+                       }
                        strncpy(d, *argv, sizeof(d)-1);
+               } else if (matches(*argv, "block") == 0) {
+                       NEXT_ARG();
+                       if (block_index)
+                               duparg("block", *argv);
+                       if (d[0]) {
+                               fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
+                               return -1;
+                       }
+                       if (get_u32(&block_index, *argv, 0) || !block_index)
+                               invarg("invalid block index value", *argv);
                } else if (strcmp(*argv, "root") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"root\" is duplicate parent ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_ROOT;
+                       req->t.tcm_parent = TC_H_ROOT;
                } else if (strcmp(*argv, "ingress") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"ingress\" is duplicate parent ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+                       req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
                                                     TC_H_MIN_INGRESS);
                } else if (strcmp(*argv, "egress") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"egress\" is duplicate parent ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+                       req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
                                                     TC_H_MIN_EGRESS);
                } else if (strcmp(*argv, "parent") == 0) {
                        __u32 handle;
 
                        NEXT_ARG();
-                       if (req.t.tcm_parent)
+                       if (req->t.tcm_parent)
                                duparg("parent", *argv);
                        if (get_tc_classid(&handle, *argv))
                                invarg("Invalid parent ID", *argv);
-                       req.t.tcm_parent = handle;
+                       req->t.tcm_parent = handle;
                } else if (strcmp(*argv, "handle") == 0) {
                        NEXT_ARG();
                        if (fhandle)
@@ -152,26 +186,29 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
                argc--; argv++;
        }
 
-       req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+       req->t.tcm_info = TC_H_MAKE(prio<<16, protocol);
 
        if (chain_index_set)
-               addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
+               addattr32(&req->n, sizeof(*req), TCA_CHAIN, chain_index);
 
        if (k[0])
-               addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
+               addattr_l(&req->n, sizeof(*req), TCA_KIND, k, strlen(k)+1);
 
        if (d[0])  {
                ll_init_map(&rth);
 
-               req.t.tcm_ifindex = ll_name_to_index(d);
-               if (req.t.tcm_ifindex == 0) {
+               req->t.tcm_ifindex = ll_name_to_index(d);
+               if (req->t.tcm_ifindex == 0) {
                        fprintf(stderr, "Cannot find device \"%s\"\n", d);
                        return 1;
                }
+       } else if (block_index) {
+               req->t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
+               req->t.tcm_block_index = block_index;
        }
 
        if (q) {
-               if (q->parse_fopt(q, fhandle, argc, argv, &req.n))
+               if (q->parse_fopt(q, fhandle, argc, argv, &req->n))
                        return 1;
        } else {
                if (fhandle) {
@@ -190,10 +227,16 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
        }
 
        if (est.ewma_log)
-               addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
+               addattr_l(&req->n, sizeof(*req), TCA_RATE, &est, sizeof(est));
 
-       if (rtnl_talk(&rth, &req.n, NULL) < 0) {
-               fprintf(stderr, "We have an error talking to the kernel\n");
+       if (buf)
+               return 0;
+
+       iov.iov_base = &req->n;
+       iov.iov_len = req->n.nlmsg_len;
+       ret = rtnl_talk_iov(&rth, &iov, 1, NULL);
+       if (ret < 0) {
+               fprintf(stderr, "We have an error talking to the kernel, %d\n", ret);
                return 2;
        }
 
@@ -206,6 +249,7 @@ static __u32 filter_prio;
 static __u32 filter_protocol;
 static __u32 filter_chain_index;
 static int filter_chain_index_set;
+static __u32 filter_block_index;
 __u16 f_proto;
 
 int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
@@ -252,20 +296,27 @@ int print_filter(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                print_bool(PRINT_ANY, "added", "added ", true);
 
        print_string(PRINT_FP, NULL, "filter ", NULL);
-       if (!filter_ifindex || filter_ifindex != t->tcm_ifindex)
-               print_string(PRINT_ANY, "dev", "dev %s ",
-                            ll_index_to_name(t->tcm_ifindex));
-
-       if (!filter_parent || filter_parent != t->tcm_parent) {
-               if (t->tcm_parent == TC_H_ROOT)
-                       print_bool(PRINT_ANY, "root", "root ", true);
-               else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS))
-                       print_bool(PRINT_ANY, "ingress", "ingress ", true);
-               else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS))
-                       print_bool(PRINT_ANY, "egress", "egress ", true);
-               else {
-                       print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
-                       print_string(PRINT_ANY, "parent", "parent %s ", abuf);
+       if (t->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
+               if (!filter_block_index ||
+                   filter_block_index != t->tcm_block_index)
+                       print_uint(PRINT_ANY, "block", "block %u ",
+                                  t->tcm_block_index);
+       } else {
+               if (!filter_ifindex || filter_ifindex != t->tcm_ifindex)
+                       print_string(PRINT_ANY, "dev", "dev %s ",
+                                    ll_index_to_name(t->tcm_ifindex));
+
+               if (!filter_parent || filter_parent != t->tcm_parent) {
+                       if (t->tcm_parent == TC_H_ROOT)
+                               print_bool(PRINT_ANY, "root", "root ", true);
+                       else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS))
+                               print_bool(PRINT_ANY, "ingress", "ingress ", true);
+                       else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS))
+                               print_bool(PRINT_ANY, "egress", "egress ", true);
+                       else {
+                               print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
+                               print_string(PRINT_ANY, "parent", "parent %s ", abuf);
+                       }
                }
        }
 
@@ -345,6 +396,7 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
        int protocol_set = 0;
        __u32 chain_index;
        int chain_index_set = 0;
+       __u32 block_index = 0;
        __u32 parent_handle = 0;
        char *fhandle = NULL;
        char  d[IFNAMSIZ] = {};
@@ -355,7 +407,21 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
                        NEXT_ARG();
                        if (d[0])
                                duparg("dev", *argv);
+                       if (block_index) {
+                               fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
+                               return -1;
+                       }
                        strncpy(d, *argv, sizeof(d)-1);
+               } else if (matches(*argv, "block") == 0) {
+                       NEXT_ARG();
+                       if (block_index)
+                               duparg("block", *argv);
+                       if (d[0]) {
+                               fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
+                               return -1;
+                       }
+                       if (get_u32(&block_index, *argv, 0) || !block_index)
+                               invarg("invalid block index value", *argv);
                } else if (strcmp(*argv, "root") == 0) {
                        if (req.t.tcm_parent) {
                                fprintf(stderr,
@@ -469,8 +535,12 @@ static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
                        return 1;
                }
                filter_ifindex = req.t.tcm_ifindex;
+       } else if (block_index) {
+               req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
+               req.t.tcm_block_index = block_index;
+               filter_block_index = block_index;
        } else {
-               fprintf(stderr, "Must specify netdevice \"dev\"\n");
+               fprintf(stderr, "Must specify netdevice \"dev\" or block index \"block\"\n");
                return -1;
        }
 
@@ -520,6 +590,7 @@ static int tc_filter_list(int argc, char **argv)
        __u32 prio = 0;
        __u32 protocol = 0;
        __u32 chain_index;
+       __u32 block_index = 0;
        char *fhandle = NULL;
 
        while (argc > 0) {
@@ -527,7 +598,21 @@ static int tc_filter_list(int argc, char **argv)
                        NEXT_ARG();
                        if (d[0])
                                duparg("dev", *argv);
+                       if (block_index) {
+                               fprintf(stderr, "Error: \"dev\" cannot be used in the same time as \"block\"\n");
+                               return -1;
+                       }
                        strncpy(d, *argv, sizeof(d)-1);
+               } else if (matches(*argv, "block") == 0) {
+                       NEXT_ARG();
+                       if (block_index)
+                               duparg("block", *argv);
+                       if (d[0]) {
+                               fprintf(stderr, "Error: \"block\" cannot be used in the same time as \"dev\"\n");
+                               return -1;
+                       }
+                       if (get_u32(&block_index, *argv, 0) || !block_index)
+                               invarg("invalid block index value", *argv);
                } else if (strcmp(*argv, "root") == 0) {
                        if (req.t.tcm_parent) {
                                fprintf(stderr,
@@ -616,6 +701,14 @@ static int tc_filter_list(int argc, char **argv)
                        return 1;
                }
                filter_ifindex = req.t.tcm_ifindex;
+       } else if (block_index) {
+               if (!tc_qdisc_block_exists(block_index)) {
+                       fprintf(stderr, "Cannot find block \"%u\"\n", block_index);
+                       return 1;
+               }
+               req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
+               req.t.tcm_block_index = block_index;
+               filter_block_index = block_index;
        }
 
        if (filter_chain_index_set)
@@ -636,20 +729,22 @@ static int tc_filter_list(int argc, char **argv)
        return 0;
 }
 
-int do_filter(int argc, char **argv)
+int do_filter(int argc, char **argv, void *buf, size_t buflen)
 {
        if (argc < 1)
                return tc_filter_list(0, NULL);
        if (matches(*argv, "add") == 0)
                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE,
-                                       argc-1, argv+1);
+                                       argc-1, argv+1, buf, buflen);
        if (matches(*argv, "change") == 0)
-               return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1);
+               return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1,
+                                       buf, buflen);
        if (matches(*argv, "replace") == 0)
                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1,
-                                       argv+1);
+                                       argv+1, buf, buflen);
        if (matches(*argv, "delete") == 0)
-               return tc_filter_modify(RTM_DELTFILTER, 0,  argc-1, argv+1);
+               return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1,
+                                       buf, buflen);
        if (matches(*argv, "get") == 0)
                return tc_filter_get(RTM_GETTFILTER, 0,  argc-1, argv+1);
        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
index 70279b9dc8767390b880f8c83e6fa84a5d769aed..8cc4b73d9d10733191db898d90d6d73b4ee66de1 100644 (file)
@@ -31,6 +31,7 @@ static int usage(void)
        fprintf(stderr, "       [ handle QHANDLE ] [ root | ingress | clsact | parent CLASSID ]\n");
        fprintf(stderr, "       [ estimator INTERVAL TIME_CONSTANT ]\n");
        fprintf(stderr, "       [ stab [ help | STAB_OPTIONS] ]\n");
+       fprintf(stderr, "       [ ingress_block BLOCK_INDEX ] [ egress_block BLOCK_INDEX ]\n");
        fprintf(stderr, "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n");
        fprintf(stderr, "\n");
        fprintf(stderr, "       tc qdisc show [ dev STRING ] [ ingress | clsact ] [ invisible ]\n");
@@ -61,6 +62,8 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
                .n.nlmsg_type = cmd,
                .t.tcm_family = AF_UNSPEC,
        };
+       __u32 ingress_block = 0;
+       __u32 egress_block = 0;
 
        while (argc > 0) {
                if (strcmp(*argv, "dev") == 0) {
@@ -121,6 +124,14 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
                        if (parse_size_table(&argc, &argv, &stab.szopts) < 0)
                                return -1;
                        continue;
+               } else if (matches(*argv, "ingress_block") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&ingress_block, *argv, 0) || !ingress_block)
+                               invarg("invalid ingress block index value", *argv);
+               } else if (matches(*argv, "egress_block") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&egress_block, *argv, 0) || !egress_block)
+                               invarg("invalid egress block index value", *argv);
                } else if (matches(*argv, "help") == 0) {
                        usage();
                } else {
@@ -138,6 +149,13 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
        if (est.ewma_log)
                addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
 
+       if (ingress_block)
+               addattr32(&req.n, sizeof(req),
+                         TCA_INGRESS_BLOCK, ingress_block);
+       if (egress_block)
+               addattr32(&req.n, sizeof(req),
+                         TCA_EGRESS_BLOCK, egress_block);
+
        if (q) {
                if (q->parse_qopt) {
                        if (q->parse_qopt(q, argc, argv, &req.n, d))
@@ -270,6 +288,24 @@ int print_qdisc(const struct sockaddr_nl *who,
            (rta_getattr_u8(tb[TCA_HW_OFFLOAD])))
                print_bool(PRINT_ANY, "offloaded", "offloaded ", true);
 
+       if (tb[TCA_INGRESS_BLOCK] &&
+           RTA_PAYLOAD(tb[TCA_INGRESS_BLOCK]) >= sizeof(__u32)) {
+               __u32 block = rta_getattr_u32(tb[TCA_INGRESS_BLOCK]);
+
+               if (block)
+                       print_uint(PRINT_ANY, "ingress_block",
+                                  "ingress_block %u ", block);
+       }
+
+       if (tb[TCA_EGRESS_BLOCK] &&
+           RTA_PAYLOAD(tb[TCA_EGRESS_BLOCK]) >= sizeof(__u32)) {
+               __u32 block = rta_getattr_u32(tb[TCA_EGRESS_BLOCK]);
+
+               if (block)
+                       print_uint(PRINT_ANY, "egress_block",
+                                  "egress_block %u ", block);
+       }
+
        /* pfifo_fast is generic enough to warrant the hardcoding --JHS */
        if (strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])) == 0)
                q = get_qdisc_kind("prio");
@@ -412,3 +448,64 @@ int do_qdisc(int argc, char **argv)
        fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv);
        return -1;
 }
+
+struct tc_qdisc_block_exists_ctx {
+       __u32 block_index;
+       bool found;
+};
+
+static int tc_qdisc_block_exists_cb(const struct sockaddr_nl *who,
+                                   struct nlmsghdr *n, void *arg)
+{
+       struct tc_qdisc_block_exists_ctx *ctx = arg;
+       struct tcmsg *t = NLMSG_DATA(n);
+       struct rtattr *tb[TCA_MAX+1];
+       int len = n->nlmsg_len;
+
+       if (n->nlmsg_type != RTM_NEWQDISC)
+               return 0;
+
+       len -= NLMSG_LENGTH(sizeof(*t));
+       if (len < 0)
+               return -1;
+
+       parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
+
+       if (tb[TCA_KIND] == NULL)
+               return -1;
+
+       if (tb[TCA_INGRESS_BLOCK] &&
+           RTA_PAYLOAD(tb[TCA_INGRESS_BLOCK]) >= sizeof(__u32)) {
+               __u32 block = rta_getattr_u32(tb[TCA_INGRESS_BLOCK]);
+
+               if (block == ctx->block_index)
+                       ctx->found = true;
+       }
+
+       if (tb[TCA_EGRESS_BLOCK] &&
+           RTA_PAYLOAD(tb[TCA_EGRESS_BLOCK]) >= sizeof(__u32)) {
+               __u32 block = rta_getattr_u32(tb[TCA_EGRESS_BLOCK]);
+
+               if (block == ctx->block_index)
+                       ctx->found = true;
+       }
+       return 0;
+}
+
+bool tc_qdisc_block_exists(__u32 block_index)
+{
+       struct tc_qdisc_block_exists_ctx ctx = { .block_index = block_index };
+       struct tcmsg t = { .tcm_family = AF_UNSPEC };
+
+       if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) {
+               perror("Cannot send dump request");
+               return false;
+       }
+
+       if (rtnl_dump_filter(&rth, tc_qdisc_block_exists_cb, &ctx) < 0) {
+               perror("Dump terminated\n");
+               return false;
+       }
+
+       return ctx.found;
+}
index 1f82ef1aec65d0a1313a43ef55f055a7e11554f4..178fe088f73260711e5ed1e9bc31644334918b59 100644 (file)
@@ -30,7 +30,9 @@ int tc_red_eval_P(unsigned int qmin, unsigned int qmax, double prob)
 {
        int i = qmax - qmin;
 
-       if (i <= 0)
+       if (!i)
+               return 0;
+       if (i < 0)
                return -1;
 
        prob /= i;
index e354765ff1ed08bf41032e62596004620adc8ddf..cd2ff5964e19938adf1eae0db81b09049278cf13 100644 (file)
@@ -134,4 +134,6 @@ void cls_names_uninit(void);
 
 int action_a2n(char *arg, int *result, bool allow_num);
 
+bool tc_qdisc_block_exists(__u32 block_index);
+
 #endif