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));
*/
#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 */
* 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 */
* @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), \
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
__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 {
__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 {
__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.
#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;
};
IFLA_CAN_TERMINATION_CONST,
IFLA_CAN_BITRATE_CONST,
IFLA_CAN_DATA_BITRATE_CONST,
+ IFLA_CAN_BITRATE_MAX,
__IFLA_CAN_MAX
};
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,
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 */
DEVLINK_DPIPE_HEADER_IPV6,
};
+enum devlink_resource_unit {
+ DEVLINK_RESOURCE_UNIT_ENTRY,
+};
+
#endif /* _LINUX_DEVLINK_H_ */
#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 */
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,
};
#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
*/
#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
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,
};
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 {
* 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, (...)
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 */
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,
TCA_DUMP_INVISIBLE,
TCA_CHAIN,
TCA_HW_OFFLOAD,
+ TCA_INGRESS_BLOCK,
+ TCA_EGRESS_BLOCK,
__TCA_MAX
};
#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
__u32 pdapi_length;
__u32 pdapi_indication;
sctp_assoc_t pdapi_assoc_id;
+ __u32 pdapi_stream;
+ __u32 pdapi_seq;
};
enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
/*
* 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
__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
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;
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
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);
}
#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);
" [ 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");
" 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);
}
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();
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;
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")) {
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")) {
* 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);
--- /dev/null
+#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,
+};
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;
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")) {
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;
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;
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);
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--;
}
static struct {
unsigned int cmd;
- struct in6_addr addr;
+ inet_prefix addr;
__u32 keyid;
const char *pass;
__u8 alg_id;
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:
} 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);
}
" [ [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"
__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) {
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);
}
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++;
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);
}
"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,
" [ [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"
__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) {
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]);
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);
}
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) {
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;
}
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]);
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,
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 = {};
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));
}
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)
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) {
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;
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));
*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 &&
errno = -err->error;
free(buf);
- return -1;
+ return -i;
}
if (answer) {
}
}
+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)
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));
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;
}
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) {
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)
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;
.RB "[ " numrxqueues
.IR QUEUE_COUNT " ]"
.br
+.BR "[ " gso_max_size
+.IR BYTES " ]"
+.RB "[ " gso_max_segs
+.IR SEGMENTS " ]"
+.br
.BI type " TYPE"
.RI "[ " ARGS " ]"
.BR lowpan " |"
.BR geneve " |"
.BR vrf " |"
-.BR macsec " ]"
+.BR macsec " |"
+.BR netdevsim " ]"
.ti -8
.IR ETYPE " := [ " TYPE " |"
.sp
.BR vrf
- Interface for L3 VRF domains
+.sp
+.BR netdevsim
+- Interface for netdev API tests
.in -8
.TP
.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.
.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 " }"
] [
.I " mode " { ip6ip | ipip | mplsip | any } "
] [
-.BR erspan " \fIIDX "
-] [
.BR external
]
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
.BI "dscp inherit"
] [
.BI dev " PHYS_DEV "
+] [
+.RB external
]
.in +8
.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
.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
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
\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
.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 " ]"
.RI "[ " OPTIONS " ]"
.B filter show dev
\fIDEV\fR
+.P
+.B tc
+.RI "[ " OPTIONS " ]"
+.B filter show block
+\fIBLOCK_INDEX\fR
.P
.ti 8
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)
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) \
{ 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)
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)
err = rd_cmd(&rd);
out:
/* Always cleanup */
- rd_free(&rd);
+ rd_cleanup(&rd);
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
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
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;
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);
}
}
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;
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;
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, '/');
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;
}
return ret;
}
-int do_action(int argc, char **argv)
+int do_action(int argc, char **argv, void *buf, size_t buflen)
{
int ret = 0;
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;
" -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)
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) {
}
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;
}
goto Exit;
}
- ret = do_cmd(argc-1, argv+1);
+ ret = do_cmd(argc-1, argv+1, NULL, 0);
Exit:
rtnl_close(&rth);
/* 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);
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);
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)
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) {
}
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;
}
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)
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);
+ }
}
}
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] = {};
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,
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;
}
__u32 prio = 0;
__u32 protocol = 0;
__u32 chain_index;
+ __u32 block_index = 0;
char *fhandle = NULL;
while (argc > 0) {
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,
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)
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
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");
.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) {
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 {
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))
(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");
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;
+}
{
int i = qmax - qmin;
- if (i <= 0)
+ if (!i)
+ return 0;
+ if (i < 0)
return -1;
prob /= i;
int action_a2n(char *arg, int *result, bool allow_num);
+bool tc_qdisc_block_exists(__u32 block_index);
+
#endif