-DNETNS_RUN_DIR=\"$(NETNS_RUN_DIR)\" \
-DNETNS_ETC_DIR=\"$(NETNS_ETC_DIR)\"
-#options for decnet
-ADDLIB+=dnet_ntop.o dnet_pton.o
-
-#options for ipx
-ADDLIB+=ipx_ntop.o ipx_pton.o
-
#options for mpls
ADDLIB+=mpls_ntop.o mpls_pton.o
+++ /dev/null
-
-Here are a few quick points about DECnet support...
-
- o iproute2 is the tool of choice for configuring the DECnet support for
- Linux. For many features, it is the only tool which can be used to
- configure them.
-
- o No name resolution is available as yet, all addresses must be
- entered numerically.
-
- o Remember to set the hardware address of the interface using:
-
- ip link set ethX address xx:xx:xx:xx:xx:xx
- (where xx:xx:xx:xx:xx:xx is the MAC address for your DECnet node
- address)
-
- if your Ethernet card won't listen to more than one unicast
- mac address at once. If the Linux DECnet stack doesn't talk to
- any other DECnet nodes, then check this with tcpdump and if its
- a problem, change the mac address (but do this _before_ starting
- any other network protocol on the interface)
-
- o Whilst you can use ip addr add to add more than one DECnet address to an
- interface, don't expect addresses which are not the same as the
- kernels node address to work properly with 2.4 kernels. This should
- be fine with 2.6 kernels as the routing code has been extensively
- modified and improved.
-
- o The DECnet support is currently self contained. It does not depend on
- the libdnet library.
-
-Steve Whitehouse <steve@chygwyn.com>
-
the linux kernel exports via a file in /proc/net/stat. In a stock 2.6.9
kernel, this is
per-protocol neighbour cache statistics
- (ipv4, ipv6, atm, decnet)
+ (ipv4, ipv6, atm)
routing cache statistics
(ipv4)
connection tracking statistics
;;
gred)
_tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
- burst probability bandwidth'
+ burst probability bandwidth ecn harddrop'
return 0
;;
hhf)
return -ENOENT;
}
-static void pr_out_param_value(struct dl *dl, int nla_type, struct nlattr *nl)
+struct param_val_conv {
+ const char *name;
+ const char *vstr;
+ uint32_t vuint;
+};
+
+static bool param_val_conv_exists(const struct param_val_conv *param_val_conv,
+ uint32_t len, const char *name)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ if (!strcmp(param_val_conv[i].name, name))
+ return true;
+
+ return false;
+}
+
+static int
+param_val_conv_uint_get(const struct param_val_conv *param_val_conv,
+ uint32_t len, const char *name, const char *vstr,
+ uint32_t *vuint)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ if (!strcmp(param_val_conv[i].name, name) &&
+ !strcmp(param_val_conv[i].vstr, vstr)) {
+ *vuint = param_val_conv[i].vuint;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static int
+param_val_conv_str_get(const struct param_val_conv *param_val_conv,
+ uint32_t len, const char *name, uint32_t vuint,
+ const char **vstr)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; i++)
+ if (!strcmp(param_val_conv[i].name, name) &&
+ param_val_conv[i].vuint == vuint) {
+ *vstr = param_val_conv[i].vstr;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static const struct param_val_conv param_val_conv[] = {
+ {
+ .name = "fw_load_policy",
+ .vstr = "driver",
+ .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
+ },
+ {
+ .name = "fw_load_policy",
+ .vstr = "flash",
+ .vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
+ },
+};
+
+#define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv)
+
+static void pr_out_param_value(struct dl *dl, const char *nla_name,
+ int nla_type, struct nlattr *nl)
{
struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
struct nlattr *val_attr;
+ const char *vstr;
+ bool conv_exists;
int err;
err = mnl_attr_parse_nested(nl, attr_cb, nla_value);
param_cmode_name(mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE])));
val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
+ conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
+ nla_name);
+
switch (nla_type) {
case MNL_TYPE_U8:
- pr_out_uint(dl, "value", mnl_attr_get_u8(val_attr));
+ if (conv_exists) {
+ err = param_val_conv_str_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ nla_name,
+ mnl_attr_get_u8(val_attr),
+ &vstr);
+ if (err)
+ return;
+ pr_out_str(dl, "value", vstr);
+ } else {
+ pr_out_uint(dl, "value", mnl_attr_get_u8(val_attr));
+ }
break;
case MNL_TYPE_U16:
- pr_out_uint(dl, "value", mnl_attr_get_u16(val_attr));
+ if (conv_exists) {
+ err = param_val_conv_str_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ nla_name,
+ mnl_attr_get_u16(val_attr),
+ &vstr);
+ if (err)
+ return;
+ pr_out_str(dl, "value", vstr);
+ } else {
+ pr_out_uint(dl, "value", mnl_attr_get_u16(val_attr));
+ }
break;
case MNL_TYPE_U32:
- pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr));
+ if (conv_exists) {
+ err = param_val_conv_str_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ nla_name,
+ mnl_attr_get_u32(val_attr),
+ &vstr);
+ if (err)
+ return;
+ pr_out_str(dl, "value", vstr);
+ } else {
+ pr_out_uint(dl, "value", mnl_attr_get_u32(val_attr));
+ }
break;
case MNL_TYPE_STRING:
pr_out_str(dl, "value", mnl_attr_get_str(val_attr));
{
struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
struct nlattr *param_value_attr;
+ const char *nla_name;
int nla_type;
int err;
nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
- pr_out_str(dl, "name",
- mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]));
+ nla_name = mnl_attr_get_str(nla_param[DEVLINK_ATTR_PARAM_NAME]);
+ pr_out_str(dl, "name", nla_name);
if (!nla_param[DEVLINK_ATTR_PARAM_GENERIC])
pr_out_str(dl, "type", "driver-specific");
mnl_attr_for_each_nested(param_value_attr,
nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
pr_out_entry_start(dl);
- pr_out_param_value(dl, nla_type, param_value_attr);
+ pr_out_param_value(dl, nla_name, nla_type, param_value_attr);
pr_out_entry_end(dl);
}
pr_out_array_end(dl);
{
struct param_ctx ctx = {};
struct nlmsghdr *nlh;
+ bool conv_exists;
uint32_t val_u32;
uint16_t val_u16;
uint8_t val_u8;
NLM_F_REQUEST | NLM_F_ACK);
dl_opts_put(nlh, dl);
+ conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
+ dl->opts.param_name);
+
mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
switch (ctx.nla_type) {
case MNL_TYPE_U8:
- err = strtouint8_t(dl->opts.param_value, &val_u8);
+ if (conv_exists) {
+ err = param_val_conv_uint_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ dl->opts.param_name,
+ dl->opts.param_value,
+ &val_u32);
+ val_u8 = val_u32;
+ } else {
+ err = strtouint8_t(dl->opts.param_value, &val_u8);
+ }
if (err)
goto err_param_value_parse;
if (val_u8 == ctx.value.vu8)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
break;
case MNL_TYPE_U16:
- err = strtouint16_t(dl->opts.param_value, &val_u16);
+ if (conv_exists) {
+ err = param_val_conv_uint_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ dl->opts.param_name,
+ dl->opts.param_value,
+ &val_u32);
+ val_u16 = val_u32;
+ } else {
+ err = strtouint16_t(dl->opts.param_value, &val_u16);
+ }
if (err)
goto err_param_value_parse;
if (val_u16 == ctx.value.vu16)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
break;
case MNL_TYPE_U32:
- err = strtouint32_t(dl->opts.param_value, &val_u32);
+ if (conv_exists)
+ err = param_val_conv_uint_get(param_val_conv,
+ PARAM_VAL_CONV_LEN,
+ dl->opts.param_name,
+ dl->opts.param_value,
+ &val_u32);
+ else
+ err = strtouint32_t(dl->opts.param_value, &val_u32);
if (err)
goto err_param_value_parse;
if (val_u32 == ctx.value.vu32)
_PRINT_FUNC(string, const char*);
_PRINT_FUNC(uint, unsigned int);
_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(hhu, unsigned char);
_PRINT_FUNC(hu, unsigned short);
_PRINT_FUNC(hex, unsigned int);
_PRINT_FUNC(0xhex, unsigned long long);
void jsonw_uint(json_writer_t *self, unsigned int number);
void jsonw_u64(json_writer_t *self, uint64_t number);
void jsonw_xint(json_writer_t *self, uint64_t number);
+void jsonw_hhu(json_writer_t *self, unsigned char num);
void jsonw_hu(json_writer_t *self, unsigned short number);
void jsonw_int(json_writer_t *self, int number);
void jsonw_s64(json_writer_t *self, int64_t number);
void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num);
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
void jsonw_int_field(json_writer_t *self, const char *prop, int num);
void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
DEVLINK_PARAM_CMODE_MAX = __DEVLINK_PARAM_CMODE_MAX - 1
};
+enum devlink_param_fw_load_policy_value {
+ DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
+ DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
+};
+
enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC,
IFLA_VXLAN_LABEL,
IFLA_VXLAN_GPE,
IFLA_VXLAN_TTL_INHERIT,
+ IFLA_VXLAN_DF,
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
__be16 high;
};
+enum ifla_vxlan_df {
+ VXLAN_DF_UNSET = 0,
+ VXLAN_DF_SET,
+ VXLAN_DF_INHERIT,
+ __VXLAN_DF_END,
+ VXLAN_DF_MAX = __VXLAN_DF_END - 1,
+};
+
/* GENEVE section */
enum {
IFLA_GENEVE_UNSPEC,
IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
IFLA_GENEVE_LABEL,
IFLA_GENEVE_TTL_INHERIT,
+ IFLA_GENEVE_DF,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
+enum ifla_geneve_df {
+ GENEVE_DF_UNSET = 0,
+ GENEVE_DF_SET,
+ GENEVE_DF_INHERIT,
+ __GENEVE_DF_END,
+ GENEVE_DF_MAX = __GENEVE_DF_END - 1,
+};
+
/* PPP section */
enum {
IFLA_PPP_UNSPEC,
TCA_FLOWER_IN_HW_COUNT,
+ TCA_FLOWER_KEY_PORT_SRC_MIN, /* be16 */
+ TCA_FLOWER_KEY_PORT_SRC_MAX, /* be16 */
+ TCA_FLOWER_KEY_PORT_DST_MIN, /* be16 */
+ TCA_FLOWER_KEY_PORT_DST_MAX, /* be16 */
+
__TCA_FLOWER_MAX,
};
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
};
+#define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */
+
/* Match-all classifier */
enum {
TCA_GRED_DPS,
TCA_GRED_MAX_P,
TCA_GRED_LIMIT,
+ TCA_GRED_VQ_LIST, /* nested TCA_GRED_VQ_ENTRY */
__TCA_GRED_MAX,
};
#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
+enum {
+ TCA_GRED_VQ_ENTRY_UNSPEC,
+ TCA_GRED_VQ_ENTRY, /* nested TCA_GRED_VQ_* */
+ __TCA_GRED_VQ_ENTRY_MAX,
+};
+#define TCA_GRED_VQ_ENTRY_MAX (__TCA_GRED_VQ_ENTRY_MAX - 1)
+
+enum {
+ TCA_GRED_VQ_UNSPEC,
+ TCA_GRED_VQ_PAD,
+ TCA_GRED_VQ_DP, /* u32 */
+ TCA_GRED_VQ_STAT_BYTES, /* u64 */
+ TCA_GRED_VQ_STAT_PACKETS, /* u32 */
+ TCA_GRED_VQ_STAT_BACKLOG, /* u32 */
+ TCA_GRED_VQ_STAT_PROB_DROP, /* u32 */
+ TCA_GRED_VQ_STAT_PROB_MARK, /* u32 */
+ TCA_GRED_VQ_STAT_FORCED_DROP, /* u32 */
+ TCA_GRED_VQ_STAT_FORCED_MARK, /* u32 */
+ TCA_GRED_VQ_STAT_PDROP, /* u32 */
+ TCA_GRED_VQ_STAT_OTHER, /* u32 */
+ TCA_GRED_VQ_FLAGS, /* u32 */
+ __TCA_GRED_VQ_MAX
+};
+
+#define TCA_GRED_VQ_MAX (__TCA_GRED_VQ_MAX - 1)
+
struct tc_gred_qopt {
__u32 limit; /* HARD maximal queue length (bytes) */
__u32 qth_min; /* Min average length threshold (bytes) */
TCA_FQ_LOW_RATE_THRESHOLD, /* per packet delay under this rate */
+ TCA_FQ_CE_THRESHOLD, /* DCTCP-like CE-marking threshold */
+
__TCA_FQ_MAX
};
__u32 inactive_flows;
__u32 throttled_flows;
__u32 unthrottle_latency_ns;
+ __u64 ce_mark; /* packets above ce_threshold */
};
/* Heavy-Hitter Filter */
#define SCTP_STREAM_SCHEDULER_VALUE 124
#define SCTP_INTERLEAVING_SUPPORTED 125
#define SCTP_SENDMSG_CONNECT 126
+#define SCTP_EVENT 127
/* PR-SCTP policies */
#define SCTP_PR_SCTP_NONE 0x0000
*/
enum sctp_sn_type {
- SCTP_SN_TYPE_BASE = (1<<15),
+ SCTP_SN_TYPE_BASE = (1<<15),
+ SCTP_DATA_IO_EVENT = SCTP_SN_TYPE_BASE,
+#define SCTP_DATA_IO_EVENT SCTP_DATA_IO_EVENT
SCTP_ASSOC_CHANGE,
#define SCTP_ASSOC_CHANGE SCTP_ASSOC_CHANGE
SCTP_PEER_ADDR_CHANGE,
#define SCTP_ASSOC_RESET_EVENT SCTP_ASSOC_RESET_EVENT
SCTP_STREAM_CHANGE_EVENT,
#define SCTP_STREAM_CHANGE_EVENT SCTP_STREAM_CHANGE_EVENT
+ SCTP_SN_TYPE_MAX = SCTP_STREAM_CHANGE_EVENT,
+#define SCTP_SN_TYPE_MAX SCTP_SN_TYPE_MAX
};
/* Notification error codes used to fill up the error fields in some
uint16_t sas_outstrms;
};
+struct sctp_event {
+ sctp_assoc_t se_assoc_id;
+ uint16_t se_type;
+ uint8_t se_on;
+};
+
/* SCTP Stream schedulers */
enum sctp_sched_type {
SCTP_SS_FCFS,
TCP_NLA_BYTES_RETRANS, /* Data bytes retransmitted */
TCP_NLA_DSACK_DUPS, /* DSACK blocks received */
TCP_NLA_REORD_SEEN, /* reordering events seen */
+ TCP_NLA_SRTT, /* smoothed RTT in usecs */
};
/* for TCP_MD5SIG socket option */
unsigned char a_addr[DN_MAXADDL];
};
-#define IPX_NODE_LEN 6
-
-struct ipx_addr {
- u_int32_t ipx_net;
- u_int8_t ipx_node[IPX_NODE_LEN];
-};
-
#ifndef AF_MPLS
# define AF_MPLS 28
#endif
int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta);
-const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
-int dnet_pton(int af, const char *src, void *addr);
-
-const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
-int ipx_pton(int af, const char *src, void *addr);
-
const char *mpls_ntop(int af, const void *addr, char *str, size_t len);
int mpls_pton(int af, const char *src, void *addr, size_t alen);
" vrf | sr }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
" -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
-" -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |\n"
+" -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
" -4 | -6 | -I | -D | -M | -B | -0 |\n"
" -l[oops] { maximum-addr-flush-attempts } | -br[ief] |\n"
" -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |\n"
preferred_family = AF_INET6;
} else if (strcmp(opt, "-0") == 0) {
preferred_family = AF_PACKET;
- } else if (strcmp(opt, "-I") == 0) {
- preferred_family = AF_IPX;
} else if (strcmp(opt, "-D") == 0) {
preferred_family = AF_DECnet;
} else if (strcmp(opt, "-M") == 0) {
" remote ADDR\n"
" [ ttl TTL ]\n"
" [ tos TOS ]\n"
+ " [ df DF ]\n"
" [ flowlabel LABEL ]\n"
" [ dstport PORT ]\n"
" [ [no]external ]\n"
" ADDR := IP_ADDRESS\n"
" TOS := { NUMBER | inherit }\n"
" TTL := { 1..255 | auto | inherit }\n"
+ " DF := { unset | set | inherit }\n"
" LABEL := 0-1048575\n"
);
}
tos = uval;
} else
tos = 1;
+ } else if (!matches(*argv, "df")) {
+ enum ifla_geneve_df df;
+
+ NEXT_ARG();
+ check_duparg(&attrs, IFLA_GENEVE_DF, "df", *argv);
+ if (strcmp(*argv, "unset") == 0)
+ df = GENEVE_DF_UNSET;
+ else if (strcmp(*argv, "set") == 0)
+ df = GENEVE_DF_SET;
+ else if (strcmp(*argv, "inherit") == 0)
+ df = GENEVE_DF_INHERIT;
+ else
+ invarg("DF must be 'unset', 'set' or 'inherit'",
+ *argv);
+
+ addattr8(n, 1024, IFLA_GENEVE_DF, df);
} else if (!matches(*argv, "label") ||
!matches(*argv, "flowlabel")) {
__u32 uval;
print_string(PRINT_FP, NULL, "tos %s ", "inherit");
}
+ if (tb[IFLA_GENEVE_DF]) {
+ enum ifla_geneve_df df = rta_getattr_u8(tb[IFLA_GENEVE_DF]);
+
+ if (df == GENEVE_DF_UNSET)
+ print_string(PRINT_JSON, "df", "df %s ", "unset");
+ else if (df == GENEVE_DF_SET)
+ print_string(PRINT_ANY, "df", "df %s ", "set");
+ else if (df == GENEVE_DF_INHERIT)
+ print_string(PRINT_ANY, "df", "df %s ", "inherit");
+ }
+
if (tb[IFLA_GENEVE_LABEL]) {
__u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]);
" [ local ADDR ]\n"
" [ ttl TTL ]\n"
" [ tos TOS ]\n"
+ " [ df DF ]\n"
" [ flowlabel LABEL ]\n"
" [ dev PHYS_DEV ]\n"
" [ dstport PORT ]\n"
" ADDR := { IP_ADDRESS | any }\n"
" TOS := { NUMBER | inherit }\n"
" TTL := { 1..255 | auto | inherit }\n"
+ " DF := { unset | set | inherit }\n"
" LABEL := 0-1048575\n"
);
}
} else
tos = 1;
addattr8(n, 1024, IFLA_VXLAN_TOS, tos);
+ } else if (!matches(*argv, "df")) {
+ enum ifla_vxlan_df df;
+
+ NEXT_ARG();
+ check_duparg(&attrs, IFLA_VXLAN_DF, "df", *argv);
+ if (strcmp(*argv, "unset") == 0)
+ df = VXLAN_DF_UNSET;
+ else if (strcmp(*argv, "set") == 0)
+ df = VXLAN_DF_SET;
+ else if (strcmp(*argv, "inherit") == 0)
+ df = VXLAN_DF_INHERIT;
+ else
+ invarg("DF must be 'unset', 'set' or 'inherit'",
+ *argv);
+
+ addattr8(n, 1024, IFLA_VXLAN_DF, df);
} else if (!matches(*argv, "label") ||
!matches(*argv, "flowlabel")) {
__u32 uval;
print_string(PRINT_FP, NULL, "ttl %s ", "auto");
}
+ if (tb[IFLA_VXLAN_DF]) {
+ enum ifla_vxlan_df df = rta_getattr_u8(tb[IFLA_VXLAN_DF]);
+
+ if (df == VXLAN_DF_UNSET)
+ print_string(PRINT_JSON, "df", "df %s ", "unset");
+ else if (df == VXLAN_DF_SET)
+ print_string(PRINT_ANY, "df", "df %s ", "set");
+ else if (df == VXLAN_DF_INHERIT)
+ print_string(PRINT_ANY, "df", "df %s ", "inherit");
+ }
+
if (tb[IFLA_VXLAN_LABEL]) {
__u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]);
"INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"
"NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n"
" [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"
- "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]\n"
+ "FAMILY := [ inet | inet6 | mpls | bridge | link ]\n"
"OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n"
" [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n"
" [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"
inet_prefix dst;
int protocol;
int protocolmask;
+ struct fib_rule_port_range sport;
+ struct fib_rule_port_range dport;
+ __u8 ipproto;
} filter;
static inline int frh_get_table(struct fib_rule_hdr *frh, struct rtattr **tb)
return false;
}
+ if (filter.ipproto) {
+ __u8 ipproto = 0;
+
+ if (tb[FRA_IP_PROTO])
+ ipproto = rta_getattr_u8(tb[FRA_IP_PROTO]);
+ if (filter.ipproto != ipproto)
+ return false;
+ }
+
+ if (filter.sport.start) {
+ const struct fib_rule_port_range *r;
+
+ if (!tb[FRA_SPORT_RANGE])
+ return false;
+
+ r = RTA_DATA(tb[FRA_SPORT_RANGE]);
+ if (r->start != filter.sport.start ||
+ r->end != filter.sport.end)
+ return false;
+ }
+
+ if (filter.dport.start) {
+ const struct fib_rule_port_range *r;
+
+ if (!tb[FRA_DPORT_RANGE])
+ return false;
+
+ r = RTA_DATA(tb[FRA_DPORT_RANGE]);
+ if (r->start != filter.dport.start ||
+ r->end != filter.dport.end)
+ return false;
+ }
+
table = frh_get_table(frh, tb);
if (filter.tb > 0 && filter.tb ^ table)
return false;
filter.protocolmask = 0;
}
filter.protocol = prot;
+ } else if (strcmp(*argv, "ipproto") == 0) {
+ int ipproto;
+
+ NEXT_ARG();
+ ipproto = inet_proto_a2n(*argv);
+ if (ipproto < 0)
+ invarg("Invalid \"ipproto\" value\n", *argv);
+ filter.ipproto = ipproto;
+ } else if (strcmp(*argv, "sport") == 0) {
+ struct fib_rule_port_range r;
+ int ret;
+
+ NEXT_ARG();
+ ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
+ if (ret == 1)
+ r.end = r.start;
+ else if (ret != 2)
+ invarg("invalid port range\n", *argv);
+ filter.sport = r;
+ } else if (strcmp(*argv, "dport") == 0) {
+ struct fib_rule_port_range r;
+ int ret;
+
+ NEXT_ARG();
+ ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
+ if (ret == 1)
+ r.end = r.start;
+ else if (ret != 2)
+ invarg("invalid dport range\n", *argv);
+ filter.dport = r;
} else{
if (matches(*argv, "dst") == 0 ||
matches(*argv, "to") == 0) {
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include "utils.h"
-
-static __inline__ u_int16_t dn_ntohs(u_int16_t addr)
-{
- union {
- u_int8_t byte[2];
- u_int16_t word;
- } u;
-
- u.word = addr;
- return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
-}
-
-static __inline__ int do_digit(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started)
-{
- u_int16_t tmp = *addr / scale;
-
- if (*pos == len)
- return 1;
-
- if (((tmp) > 0) || *started || (scale == 1)) {
- *str = tmp + '0';
- *started = 1;
- (*pos)++;
- *addr -= (tmp * scale);
- }
-
- return 0;
-}
-
-
-static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len)
-{
- u_int16_t addr, area;
- size_t pos = 0;
- int started = 0;
-
- memcpy(&addr, dna->a_addr, sizeof(addr));
- addr = dn_ntohs(addr);
- area = addr >> 10;
-
- if (dna->a_len != 2)
- return NULL;
-
- addr &= 0x03ff;
-
- if (len == 0)
- return str;
-
- if (do_digit(str + pos, &area, 10, &pos, len, &started))
- return str;
-
- if (do_digit(str + pos, &area, 1, &pos, len, &started))
- return str;
-
- if (pos == len)
- return str;
-
- *(str + pos) = '.';
- pos++;
- started = 0;
-
- if (do_digit(str + pos, &addr, 1000, &pos, len, &started))
- return str;
-
- if (do_digit(str + pos, &addr, 100, &pos, len, &started))
- return str;
-
- if (do_digit(str + pos, &addr, 10, &pos, len, &started))
- return str;
-
- if (do_digit(str + pos, &addr, 1, &pos, len, &started))
- return str;
-
- if (pos == len)
- return str;
-
- *(str + pos) = 0;
-
- return str;
-}
-
-
-const char *dnet_ntop(int af, const void *addr, char *str, size_t len)
-{
- switch(af) {
- case AF_DECnet:
- errno = 0;
- return dnet_ntop1((struct dn_naddr *)addr, str, len);
- default:
- errno = EAFNOSUPPORT;
- }
-
- return NULL;
-}
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <netinet/in.h>
-
-#include "utils.h"
-
-static __inline__ u_int16_t dn_htons(u_int16_t addr)
-{
- union {
- u_int8_t byte[2];
- u_int16_t word;
- } u;
-
- u.word = addr;
- return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8);
-}
-
-
-static int dnet_num(const char *src, u_int16_t * dst)
-{
- int rv = 0;
- int tmp;
- *dst = 0;
-
- while ((tmp = *src++) != 0) {
- tmp -= '0';
- if ((tmp < 0) || (tmp > 9))
- return rv;
-
- rv++;
- (*dst) *= 10;
- (*dst) += tmp;
- }
-
- return rv;
-}
-
-static int dnet_pton1(const char *src, struct dn_naddr *dna)
-{
- u_int16_t addr;
- u_int16_t area = 0;
- u_int16_t node = 0;
- int pos;
-
- pos = dnet_num(src, &area);
- if ((pos == 0) || (area > 63) || (*(src + pos) != '.'))
- return 0;
- pos = dnet_num(src + pos + 1, &node);
- if ((pos == 0) || (node > 1023))
- return 0;
- dna->a_len = 2;
- addr = dn_htons((area << 10) | node);
- memcpy(dna->a_addr, &addr, sizeof(addr));
-
- return 1;
-}
-
-int dnet_pton(int af, const char *src, void *addr)
-{
- int err;
-
- switch (af) {
- case AF_DECnet:
- errno = 0;
- err = dnet_pton1(src, (struct dn_naddr *)addr);
- break;
- default:
- errno = EAFNOSUPPORT;
- err = -1;
- }
-
- return err;
-}
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include "utils.h"
-
-static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len)
-{
- u_int32_t tmp = addr >> (scale * 4);
-
- if (*pos == len)
- return 1;
-
- tmp &= 0x0f;
- if (tmp > 9)
- *str = tmp + 'A' - 10;
- else
- *str = tmp + '0';
- (*pos)++;
-
- return 0;
-}
-
-static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len)
-{
- int i;
- size_t pos = 0;
-
- if (len == 0)
- return str;
-
- for(i = 7; i >= 0; i--)
- if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len))
- return str;
-
- if (pos == len)
- return str;
-
- *(str + pos) = '.';
- pos++;
-
- for(i = 0; i < 6; i++) {
- if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len))
- return str;
- if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len))
- return str;
- }
-
- if (pos == len)
- return str;
-
- *(str + pos) = 0;
-
- return str;
-}
-
-
-const char *ipx_ntop(int af, const void *addr, char *str, size_t len)
-{
- switch(af) {
- case AF_IPX:
- errno = 0;
- return ipx_ntop1((struct ipx_addr *)addr, str, len);
- default:
- errno = EAFNOSUPPORT;
- }
-
- return NULL;
-}
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include "utils.h"
-
-static int ipx_getnet(u_int32_t *net, const char *str)
-{
- int i;
- u_int32_t tmp;
-
- for(i = 0; *str && (i < 8); i++) {
-
- if ((tmp = get_hex(*str)) == -1) {
- if (*str == '.')
- return 0;
- else
- return -1;
- }
-
- str++;
- (*net) <<= 4;
- (*net) |= tmp;
- }
-
- if (*str == 0)
- return 0;
-
- return -1;
-}
-
-static int ipx_getnode(u_int8_t *node, const char *str)
-{
- int i;
- u_int32_t tmp;
-
- for(i = 0; i < 6; i++) {
- if ((tmp = get_hex(*str++)) == -1)
- return -1;
- node[i] = (u_int8_t)tmp;
- node[i] <<= 4;
- if ((tmp = get_hex(*str++)) == -1)
- return -1;
- node[i] |= (u_int8_t)tmp;
- if (*str == ':')
- str++;
- }
-
- return 0;
-}
-
-static int ipx_pton1(const char *src, struct ipx_addr *addr)
-{
- char *sep = (char *)src;
- int no_node = 0;
-
- memset(addr, 0, sizeof(struct ipx_addr));
-
- while(*sep && (*sep != '.'))
- sep++;
-
- if (*sep != '.')
- no_node = 1;
-
- if (ipx_getnet(&addr->ipx_net, src))
- return 0;
-
- addr->ipx_net = htonl(addr->ipx_net);
-
- if (no_node)
- return 1;
-
- if (ipx_getnode(addr->ipx_node, sep + 1))
- return 0;
-
- return 1;
-}
-
-int ipx_pton(int af, const char *src, void *addr)
-{
- int err;
-
- switch (af) {
- case AF_IPX:
- errno = 0;
- err = ipx_pton1(src, (struct ipx_addr *)addr);
- break;
- default:
- errno = EAFNOSUPPORT;
- err = -1;
- }
-
- return err;
-}
}
_PRINT_FUNC(int, int);
_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(hhu, unsigned char);
_PRINT_FUNC(hu, unsigned short);
_PRINT_FUNC(uint, unsigned int);
_PRINT_FUNC(u64, uint64_t);
jsonw_printf(self, "%g", num);
}
+void jsonw_hhu(json_writer_t *self, unsigned char num)
+{
+ jsonw_printf(self, "%hhu", num);
+}
+
void jsonw_hu(json_writer_t *self, unsigned short num)
{
jsonw_printf(self, "%hu", num);
jsonw_xint(self, num);
}
+void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
+{
+ jsonw_name(self, prop);
+ jsonw_hhu(self, num);
+}
+
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
{
jsonw_name(self, prop);
return 0;
}
- if (family == AF_DECnet) {
- struct dn_naddr dna;
-
- addr->family = AF_DECnet;
- if (dnet_pton(AF_DECnet, name, &dna) <= 0)
- return -1;
- memcpy(addr->data, dna.a_addr, 2);
- addr->bytelen = 2;
- addr->bitlen = -1;
- return 0;
- }
-
if (family == AF_MPLS) {
unsigned int maxlabels;
int i;
return inet_ntop(af, addr, buf, buflen);
case AF_MPLS:
return mpls_ntop(af, addr, buf, buflen);
- case AF_IPX:
- return ipx_ntop(af, addr, buf, buflen);
- case AF_DECnet:
- {
- struct dn_naddr dna = { 2, { 0, 0, } };
-
- memcpy(dna.a_addr, addr, 2);
- return dnet_ntop(af, &dna, buf, buflen);
- }
case AF_PACKET:
return ll_addr_n2a(addr, len, ARPHRD_VOID, buf, buflen);
case AF_BRIDGE:
family = AF_INET;
else if (strcmp(name, "inet6") == 0)
family = AF_INET6;
- else if (strcmp(name, "dnet") == 0)
- family = AF_DECnet;
else if (strcmp(name, "link") == 0)
family = AF_PACKET;
else if (strcmp(name, "ipx") == 0)
return "inet";
if (family == AF_INET6)
return "inet6";
- if (family == AF_DECnet)
- return "dnet";
if (family == AF_PACKET)
return "link";
if (family == AF_IPX)
] [
.BI tos " TOS "
] [
+.BI df " DF "
+] [
.BI flowlabel " FLOWLABEL "
] [
.BI dstport " PORT "
.BI tos " TOS"
- specifies the TOS value to use in outgoing packets.
+.sp
+.BI df " DF"
+- specifies the usage of the Don't Fragment flag (DF) bit in outgoing packets
+with IPv4 headers. The value
+.B inherit
+causes the bit to be copied from the original IP header. The values
+.B unset
+and
+.B set
+cause the bit to be always unset or always set, respectively. By default, the
+bit is not set.
+
.sp
.BI flowlabel " FLOWLABEL"
- specifies the flow label to use in outgoing packets.
] [
.BI tos " TOS "
] [
+.BI df " DF "
+] [
.BI flowlabel " FLOWLABEL "
] [
.BI dstport " PORT"
.BI tos " TOS"
- specifies the TOS value to use in outgoing packets.
+.sp
+.BI df " DF"
+- specifies the usage of the Don't Fragment flag (DF) bit in outgoing packets
+with IPv4 headers. The value
+.B inherit
+causes the bit to be copied from the original IP header. The values
+.B unset
+and
+.B set
+cause the bit to be always unset or always set, respectively. By default, the
+bit is not set.
+
.sp
.BI flowlabel " FLOWLABEL"
- specifies the flow label to use in outgoing packets.
.ti -8
.IR FAMILY " := [ "
-.BR inet " | " inet6 " | " ipx " | " dnet " | " mpls " | " bridge " | " link " ]"
+.BR inet " | " inet6 " | " mpls " | " bridge " | " link " ]"
.ti -8
.IR OPTIONS " := " FLAGS " [ "
\fB\-r\fR[\fIesolve\fR] |
\fB\-iec\fR |
\fB\-f\fR[\fIamily\fR] {
-.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
+.BR inet " | " inet6 " | " link " } | "
\fB-4\fR |
\fB-6\fR |
\fB-I\fR |
.TP
.BR "\-f" , " \-family " <FAMILY>
Specifies the protocol family to use. The protocol family identifier can be one of
-.BR "inet" , " inet6" , " bridge" , " ipx" , " dnet" , " mpls"
+.BR "inet" , " inet6" , " bridge" , " mpls"
or
.BR link .
If this option is not present,
shortcut for
.BR "\-family bridge" .
-.TP
-.B \-D
-shortcut for
-.BR "\-family decnet" .
-
-.TP
-.B \-I
-shortcut for
-.BR "\-family ipx" .
-
.TP
.B \-M
shortcut for
.TH RDMA\-DEV 8 "06 Jul 2017" "iproute2" "Linux"
.SH NAME
-rdmak-dev \- RDMA device configuration
+rdma-dev \- RDMA device configuration
.SH SYNOPSIS
.sp
.ad l
.B rdma dev show
.RI "[ " DEV " ]"
+.ti -8
+.B rdma dev set
+.RI "[ " DEV " ]"
+.BR name
+.BR NEWNAME
+
.ti -8
.B rdma dev help
.SH "DESCRIPTION"
+.SS rdma dev set - rename rdma device
+
.SS rdma dev show - display rdma device attributes
.PP
Shows the state of specified RDMA device.
.RE
.PP
+rdma dev set mlx5_3 name rdma_0
+.RS 4
+Renames the mlx5_3 device to rdma_0.
+.RE
+.PP
.SH SEE ALSO
.BR rdma (8),
.IR MASKED_IP_TTL " | { "
.BR dst_ip " | " src_ip " } "
.IR PREFIX " | { "
-.BR dst_port " | " src_port " } "
-.IR port_number " } | "
+.BR dst_port " | " src_port " } { "
+.IR port_number " | "
+.IR min_port_number-max_port_number " } | "
.B tcp_flags
.IR MASKED_TCP_FLAGS " | "
.B type
option to tc filter, optionally followed by a slash and the prefix length.
If the prefix is missing, \fBtc\fR assumes a full-length host match.
.TP
-.BI dst_port " NUMBER"
+.IR \fBdst_port " { " NUMBER " | " " MIN_VALUE-MAX_VALUE " }
.TQ
-.BI src_port " NUMBER"
-Match on layer 4 protocol source or destination port number. Only available for
+.IR \fBsrc_port " { " NUMBER " | " " MIN_VALUE-MAX_VALUE " }
+Match on layer 4 protocol source or destination port number. Alternatively, the
+mininum and maximum values can be specified to match on a range of layer 4
+protocol source or destination port numbers. Only available for
.BR ip_proto " values " udp ", " tcp " and " sctp
which have to be specified in beforehand.
.TP
.B maxrate
RATE ] [
.B buckets
-NUMBER ] [
+NUMBER ] [
+.B orphan_mask
+NUMBER ] [
.B pacing
|
.B nopacing
-]
+] [
+.B ce_threshold
+TIME ]
.SH DESCRIPTION
FQ (Fair Queue) is a classless packet scheduler meant to be mostly
used for locally generated traffic. It is designed to achieve per flow pacing.
FQ does flow separation, and is able to respect pacing requirements set by TCP stack.
All packets belonging to a socket are considered as a 'flow'.
-For non local packets (router workload), packet rxhash is used as fallback.
+For non local packets (router workload), packet hash is used as fallback.
An application can specify a maximum pacing rate using the
.B SO_MAX_PACING_RATE
setsockopt call. This packet scheduler adds delay between packets to
-respect rate limitation set by TCP stack.
+respect rate limitation set on each socket. Note that after linux-4.20, linux adopted EDT (Earliest Departure Time)
+and TCP directly sets the appropriate Departure Time for each skb.
Dequeueing happens in a round-robin fashion.
A special FIFO queue is reserved for high priority packets (
The size of the hash table used for flow lookups. Each bucket is assigned a
red-black tree for efficient collision sorting.
Default: 1024.
+.SS orphan_mask
+For packets not owned by a socket, fq is able to mask a part of skb->hash
+and reduce number of buckets associated with the traffic. This is a DDOS
+prevention mechanism, and the default is 1023 (meaning no more than 1024 flows
+are allocated for these packets)
.SS [no]pacing
Enable or disable flow pacing. Default is enabled.
+.SS ce_threshold
+sets a threshold above which all packets are marked with ECN Congestion
+Experienced. This is useful for DCTCP-style congestion control algorithms that
+require marking at very shallow queueing thresholds.
+
.SH EXAMPLES
-#tc qdisc add dev eth0 root fq
+#tc qdisc add dev eth0 root est 1sec 4sec fq ce_threshold 4ms
.br
-#tc -s -d qdisc
+#tc -s -d qdisc sh dev eth0
.br
-qdisc fq 8003: dev eth0 root refcnt 2 limit 10000p flow_limit 100p buckets 1024 quantum 3028 initial_quantum 15140
- Sent 503727981 bytes 1146972 pkt (dropped 0, overlimits 0 requeues 54452)
- backlog 0b 0p requeues 54452
- 1289 flows (1289 inactive, 0 throttled)
- 0 gc, 31 highprio, 27411 throttled
+qdisc fq 800e: root refcnt 9 limit 10000p flow_limit 1000p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 low_rate_threshold 550Kbit refill_delay 40.0ms ce_threshold 4.0ms
+ Sent 533368436185 bytes 352296695 pkt (dropped 0, overlimits 0 requeues 1339864)
+ rate 39220Mbit 3238202pps backlog 12417828b 358p requeues 1339864
+ 1052 flows (852 inactive, 0 throttled)
+ 112 gc, 0 highprio, 212 throttled, 21501 ns latency, 470241 ce_mark
.br
.SH SEE ALSO
.BR tc (8),
static int dev_help(struct rd *rd)
{
pr_out("Usage: %s dev show [DEV]\n", rd->filename);
+ pr_out(" %s dev set [DEV] name DEVNAME\n", rd->filename);
return 0;
}
return rd_exec_cmd(rd, cmds, "parameter");
}
+static int dev_set_name(struct rd *rd)
+{
+ uint32_t seq;
+
+ if (rd_no_arg(rd)) {
+ pr_err("Please provide device new name.\n");
+ return -EINVAL;
+ }
+
+ rd_prepare_msg(rd, RDMA_NLDEV_CMD_SET,
+ &seq, (NLM_F_REQUEST | NLM_F_ACK));
+ mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
+ mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_DEV_NAME, rd_argv(rd));
+
+ return rd_send_msg(rd);
+}
+
+static int dev_one_set(struct rd *rd)
+{
+ const struct rd_cmd cmds[] = {
+ { NULL, dev_help},
+ { "name", dev_set_name},
+ { 0 }
+ };
+
+ return rd_exec_cmd(rd, cmds, "parameter");
+}
+
static int dev_show(struct rd *rd)
{
return rd_exec_dev(rd, dev_one_show);
}
+static int dev_set(struct rd *rd)
+{
+ return rd_exec_require_dev(rd, dev_one_set);
+}
+
int cmd_dev(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_show },
{ "show", dev_show },
{ "list", dev_show },
+ { "set", dev_set },
{ "help", dev_help },
{ 0 }
};
int (*func)(struct rd *rd);
};
+/*
+ * Parser interface
+ */
+bool rd_no_arg(struct rd *rd);
+void rd_arg_inc(struct rd *rd);
+
+char *rd_argv(struct rd *rd);
/*
* Commands interface
int cmd_res(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_require_dev(struct rd *rd, int (*cb)(struct rd *rd));
int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port);
void rd_free(struct rd *rd);
int rd_set_arg_to_devname(struct rd *rd);
int rd_argc(struct rd *rd);
+int strcmpx(const char *str1, const char *str2);
+
/*
* Device manipulation
*/
void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags);
int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data);
int rd_attr_cb(const struct nlattr *attr, void *data);
+int rd_attr_check(const struct nlattr *attr, int *typep);
/*
* Print helpers
*/
void print_driver_table(struct rd *rd, struct nlattr *tb);
void newline(struct rd *rd);
+void newline_indent(struct rd *rd);
#define MAX_LINE_LENGTH 80
#endif /* _RDMA_TOOL_H_ */
return rd->argc;
}
-static char *rd_argv(struct rd *rd)
+char *rd_argv(struct rd *rd)
{
if (!rd_argc(rd))
return NULL;
return *rd->argv;
}
-static int strcmpx(const char *str1, const char *str2)
+int strcmpx(const char *str1, const char *str2)
{
if (strlen(str1) > strlen(str2))
return -1;
return strcmpx(rd_argv(rd), pattern) == 0;
}
-static void rd_arg_inc(struct rd *rd)
+void rd_arg_inc(struct rd *rd)
{
if (!rd_argc(rd))
return;
rd->argv++;
}
-static bool rd_no_arg(struct rd *rd)
+bool rd_no_arg(struct rd *rd)
{
return rd_argc(rd) == 0;
}
[RDMA_NLDEV_ATTR_DRIVER_U64] = MNL_TYPE_U64,
};
-static int rd_attr_check(const struct nlattr *attr, int *typep)
+int rd_attr_check(const struct nlattr *attr, int *typep)
{
int type;
return ret;
}
+int rd_exec_require_dev(struct rd *rd, int (*cb)(struct rd *rd))
+{
+ if (rd_no_arg(rd)) {
+ pr_err("Please provide device name.\n");
+ return -EINVAL;
+ }
+
+ return rd_exec_dev(rd, cb);
+}
+
int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str)
{
const struct rd_cmd *c;
pr_out("\n");
}
-static void newline_indent(struct rd *rd)
+void newline_indent(struct rd *rd)
{
newline(rd);
if (!rd->json_output)
return -1;
}
+static int flower_port_range_attr_type(__u8 ip_proto, enum flower_endpoint type,
+ __be16 *min_port_type,
+ __be16 *max_port_type)
+{
+ if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP ||
+ ip_proto == IPPROTO_SCTP) {
+ if (type == FLOWER_ENDPOINT_SRC) {
+ *min_port_type = TCA_FLOWER_KEY_PORT_SRC_MIN;
+ *max_port_type = TCA_FLOWER_KEY_PORT_SRC_MAX;
+ } else {
+ *min_port_type = TCA_FLOWER_KEY_PORT_DST_MIN;
+ *max_port_type = TCA_FLOWER_KEY_PORT_DST_MAX;
+ }
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int flower_parse_port(char *str, __u8 ip_proto,
enum flower_endpoint endpoint,
struct nlmsghdr *n)
{
+ __u16 min, max;
int ret;
- int type;
- __be16 port;
- type = flower_port_attr_type(ip_proto, endpoint);
- if (type < 0)
- return -1;
+ ret = sscanf(str, "%hu-%hu", &min, &max);
- ret = get_be16(&port, str, 10);
- if (ret)
- return -1;
+ if (ret == 1) {
+ int type;
- addattr16(n, MAX_MSG, type, port);
+ type = flower_port_attr_type(ip_proto, endpoint);
+ if (type < 0)
+ return -1;
+ addattr16(n, MAX_MSG, type, htons(min));
+ } else if (ret == 2) {
+ __be16 min_port_type, max_port_type;
+ if (max <= min) {
+ fprintf(stderr, "max value should be greater than min value\n");
+ return -1;
+ }
+ if (flower_port_range_attr_type(ip_proto, endpoint,
+ &min_port_type, &max_port_type))
+ return -1;
+
+ addattr16(n, MAX_MSG, min_port_type, htons(min));
+ addattr16(n, MAX_MSG, max_port_type, htons(max));
+ } else {
+ return -1;
+ }
return 0;
}
print_hu(PRINT_ANY, name, namefrm, rta_getattr_be16(attr));
}
+static void flower_print_port_range(char *name, struct rtattr *min_attr,
+ struct rtattr *max_attr)
+{
+ if (!min_attr || !max_attr)
+ return;
+
+ if (is_json_context()) {
+ open_json_object(name);
+ print_hu(PRINT_JSON, "start", NULL, rta_getattr_be16(min_attr));
+ print_hu(PRINT_JSON, "end", NULL, rta_getattr_be16(max_attr));
+ close_json_object();
+ } else {
+ SPRINT_BUF(namefrm);
+ SPRINT_BUF(out);
+ size_t done;
+
+ done = sprintf(out, "%u", rta_getattr_be16(min_attr));
+ sprintf(out + done, "-%u", rta_getattr_be16(max_attr));
+ sprintf(namefrm, "\n %s %%s", name);
+ print_string(PRINT_ANY, name, namefrm, out);
+ }
+}
+
static void flower_print_tcp_flags(const char *name, struct rtattr *flags_attr,
struct rtattr *mask_attr)
{
struct rtattr *opt, __u32 handle)
{
struct rtattr *tb[TCA_FLOWER_MAX + 1];
+ __be16 min_port_type, max_port_type;
int nl_type, nl_mask_type;
__be16 eth_type = 0;
__u8 ip_proto = 0xff;
if (nl_type >= 0)
flower_print_port("src_port", tb[nl_type]);
+ if (!flower_port_range_attr_type(ip_proto, FLOWER_ENDPOINT_DST,
+ &min_port_type, &max_port_type))
+ flower_print_port_range("dst_port",
+ tb[min_port_type], tb[max_port_type]);
+
+ if (!flower_port_range_attr_type(ip_proto, FLOWER_ENDPOINT_SRC,
+ &min_port_type, &max_port_type))
+ flower_print_port_range("src_port",
+ tb[min_port_type], tb[max_port_type]);
+
flower_print_tcp_flags("tcp_flags", tb[TCA_FLOWER_KEY_TCP_FLAGS],
tb[TCA_FLOWER_KEY_TCP_FLAGS_MASK]);
fprintf(f, "limit %up min %up max %up ",
qopt->limit, qopt->qth_min, qopt->qth_max);
- if (qopt->flags & TC_RED_ECN)
- fprintf(f, "ecn ");
+ tc_red_print_flags(qopt->flags);
if (show_details) {
fprintf(f, "ewma %u ", qopt->Wlog);
fprintf(stderr, " [ [no]pacing ] [ refill_delay TIME ]\n");
fprintf(stderr, " [ low_rate_threshold RATE ]\n");
fprintf(stderr, " [ orphan_mask MASK]\n");
+ fprintf(stderr, " [ ce_threshold TIME ]\n");
}
static unsigned int ilog2(unsigned int val)
unsigned int defrate;
unsigned int refill_delay;
unsigned int orphan_mask;
+ unsigned int ce_threshold;
bool set_plimit = false;
bool set_flow_plimit = false;
bool set_quantum = false;
bool set_refill_delay = false;
bool set_orphan_mask = false;
bool set_low_rate_threshold = false;
+ bool set_ce_threshold = false;
int pacing = -1;
struct rtattr *tail;
return -1;
}
set_low_rate_threshold = true;
+ } else if (strcmp(*argv, "ce_threshold") == 0) {
+ NEXT_ARG();
+ if (get_time(&ce_threshold, *argv)) {
+ fprintf(stderr, "Illegal \"ce_threshold\"\n");
+ return -1;
+ }
+ set_ce_threshold = true;
} else if (strcmp(*argv, "defrate") == 0) {
NEXT_ARG();
if (strchr(*argv, '%')) {
if (set_orphan_mask)
addattr_l(n, 1024, TCA_FQ_ORPHAN_MASK,
&orphan_mask, sizeof(refill_delay));
+ if (set_ce_threshold)
+ addattr_l(n, 1024, TCA_FQ_CE_THRESHOLD,
+ &ce_threshold, sizeof(ce_threshold));
addattr_nest_end(n, tail);
return 0;
}
unsigned int rate, quantum;
unsigned int refill_delay;
unsigned int orphan_mask;
+ unsigned int ce_threshold;
SPRINT_BUF(b1);
fprintf(f, "refill_delay %s ", sprint_time(refill_delay, b1));
}
+ if (tb[TCA_FQ_CE_THRESHOLD] &&
+ RTA_PAYLOAD(tb[TCA_FQ_CE_THRESHOLD]) >= sizeof(__u32)) {
+ ce_threshold = rta_getattr_u32(tb[TCA_FQ_CE_THRESHOLD]);
+ if (ce_threshold != ~0U)
+ fprintf(f, "ce_threshold %s ", sprint_time(ce_threshold, b1));
+ }
+
return 0;
}
static int fq_print_xstats(struct qdisc_util *qu, FILE *f,
struct rtattr *xstats)
{
- struct tc_fq_qd_stats *st;
+ struct tc_fq_qd_stats *st, _st;
if (xstats == NULL)
return 0;
- if (RTA_PAYLOAD(xstats) < sizeof(*st))
- return -1;
+ memset(&_st, 0, sizeof(_st));
+ memcpy(&_st, RTA_DATA(xstats), min(RTA_PAYLOAD(xstats), sizeof(*st)));
- st = RTA_DATA(xstats);
+ st = &_st;
fprintf(f, " %u flows (%u inactive, %u throttled)",
st->flows, st->inactive_flows, st->throttled_flows);
if (st->unthrottle_latency_ns)
fprintf(f, ", %u ns latency", st->unthrottle_latency_ns);
+ if (st->ce_mark)
+ fprintf(f, ", %llu ce_mark", st->ce_mark);
+
if (st->flows_plimit)
fprintf(f, ", %llu flows_plimit", st->flows_plimit);
static void explain(void)
{
fprintf(stderr, "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n");
- fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ]\n");
+ fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ] [ecn] [harddrop]\n");
fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n");
fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n");
- fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n");
+ fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ] [ecn] [harddrop]\n");
}
static int init_gred(struct qdisc_util *qu, int argc, char **argv,
fprintf(stderr, "Illegal \"limit\"\n");
return -1;
}
+ } else if (strcmp(*argv, "ecn") == 0) {
+ opt.flags |= TC_RED_ECN;
+ } else if (strcmp(*argv, "harddrop") == 0) {
+ opt.flags |= TC_RED_HARDDROP;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
*/
static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev)
{
+ struct rtattr *tail, *entry, *vqs;
int ok = 0;
struct tc_gred_qopt opt = { 0 };
unsigned int burst = 0;
unsigned int avpkt = 0;
+ unsigned int flags = 0;
double probability = 0.02;
unsigned int rate = 0;
int parm;
__u8 sbuf[256];
- struct rtattr *tail;
__u32 max_P;
opt.DP = MAX_DPs;
return -1;
}
ok++;
+ } else if (strcmp(*argv, "ecn") == 0) {
+ flags |= TC_RED_ECN;
+ } else if (strcmp(*argv, "harddrop") == 0) {
+ flags |= TC_RED_HARDDROP;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256);
max_P = probability * pow(2, 32);
addattr32(n, 1024, TCA_GRED_MAX_P, max_P);
+
+ vqs = addattr_nest(n, 1024, TCA_GRED_VQ_LIST);
+ entry = addattr_nest(n, 1024, TCA_GRED_VQ_ENTRY);
+ addattr32(n, 1024, TCA_GRED_VQ_DP, opt.DP);
+ addattr32(n, 1024, TCA_GRED_VQ_FLAGS, flags);
+ addattr_nest_end(n, entry);
+ addattr_nest_end(n, vqs);
+
addattr_nest_end(n, tail);
return 0;
}
+struct tc_gred_info {
+ bool flags_present;
+ __u64 bytes;
+ __u32 packets;
+ __u32 backlog;
+ __u32 prob_drop;
+ __u32 prob_mark;
+ __u32 forced_drop;
+ __u32 forced_mark;
+ __u32 pdrop;
+ __u32 other;
+ __u32 flags;
+};
+
+static void
+gred_parse_vqs(struct tc_gred_info *info, struct rtattr *vqs)
+{
+ int rem = RTA_PAYLOAD(vqs);
+ unsigned int offset = 0;
+
+ while (rem > offset) {
+ struct rtattr *tb_entry[TCA_GRED_VQ_ENTRY_MAX + 1] = {};
+ struct rtattr *tb[TCA_GRED_VQ_MAX + 1] = {};
+ struct rtattr *entry;
+ unsigned int len;
+ unsigned int dp;
+
+ entry = RTA_DATA(vqs) + offset;
+
+ parse_rtattr(tb_entry, TCA_GRED_VQ_ENTRY_MAX, entry,
+ rem - offset);
+ len = RTA_LENGTH(RTA_PAYLOAD(entry));
+ offset += len;
+
+ if (!tb_entry[TCA_GRED_VQ_ENTRY]) {
+ fprintf(stderr,
+ "ERROR: Failed to parse Virtual Queue entry\n");
+ continue;
+ }
+
+ parse_rtattr_nested(tb, TCA_GRED_VQ_MAX,
+ tb_entry[TCA_GRED_VQ_ENTRY]);
+
+ if (!tb[TCA_GRED_VQ_DP]) {
+ fprintf(stderr,
+ "ERROR: Virtual Queue without DP attribute\n");
+ continue;
+ }
+
+ dp = rta_getattr_u32(tb[TCA_GRED_VQ_DP]);
+
+ if (tb[TCA_GRED_VQ_STAT_BYTES])
+ info[dp].bytes =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BYTES]);
+ if (tb[TCA_GRED_VQ_STAT_PACKETS])
+ info[dp].packets =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PACKETS]);
+ if (tb[TCA_GRED_VQ_STAT_BACKLOG])
+ info[dp].backlog =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_BACKLOG]);
+ if (tb[TCA_GRED_VQ_STAT_PROB_DROP])
+ info[dp].prob_drop =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_DROP]);
+ if (tb[TCA_GRED_VQ_STAT_PROB_MARK])
+ info[dp].prob_mark =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PROB_MARK]);
+ if (tb[TCA_GRED_VQ_STAT_FORCED_DROP])
+ info[dp].forced_drop =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_DROP]);
+ if (tb[TCA_GRED_VQ_STAT_FORCED_MARK])
+ info[dp].forced_mark =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_FORCED_MARK]);
+ if (tb[TCA_GRED_VQ_STAT_PDROP])
+ info[dp].pdrop =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_PDROP]);
+ if (tb[TCA_GRED_VQ_STAT_OTHER])
+ info[dp].other =
+ rta_getattr_u32(tb[TCA_GRED_VQ_STAT_OTHER]);
+ info[dp].flags_present = !!tb[TCA_GRED_VQ_FLAGS];
+ if (tb[TCA_GRED_VQ_FLAGS])
+ info[dp].flags =
+ rta_getattr_u32(tb[TCA_GRED_VQ_FLAGS]);
+ }
+}
+
+static void
+gred_print_stats(struct tc_gred_info *info, struct tc_gred_qopt *qopt)
+{
+ __u64 bytes = info ? info->bytes : qopt->bytesin;
+
+ SPRINT_BUF(b1);
+
+ if (!is_json_context())
+ printf("\n Queue size: ");
+
+ print_uint(PRINT_JSON, "qave", NULL, qopt->qave);
+ print_string(PRINT_FP, NULL, "average %s ",
+ sprint_size(qopt->qave, b1));
+
+ print_uint(PRINT_JSON, "backlog", NULL, qopt->backlog);
+ print_string(PRINT_FP, NULL, "current %s ",
+ sprint_size(qopt->backlog, b1));
+
+ if (!is_json_context())
+ printf("\n Dropped packets: ");
+
+ if (info) {
+ print_uint(PRINT_ANY, "forced_drop", "forced %u ",
+ info->forced_drop);
+ print_uint(PRINT_ANY, "prob_drop", "early %u ",
+ info->prob_drop);
+ print_uint(PRINT_ANY, "pdrop", "pdrop %u ", info->pdrop);
+ print_uint(PRINT_ANY, "other", "other %u ", info->other);
+
+ if (!is_json_context())
+ printf("\n Marked packets: ");
+ print_uint(PRINT_ANY, "forced_mark", "forced %u ",
+ info->forced_mark);
+ print_uint(PRINT_ANY, "prob_mark", "early %u ",
+ info->prob_mark);
+ } else {
+ print_uint(PRINT_ANY, "forced_drop", "forced %u ",
+ qopt->forced);
+ print_uint(PRINT_ANY, "prob_drop", "early %u ", qopt->early);
+ print_uint(PRINT_ANY, "pdrop", "pdrop %u ", qopt->pdrop);
+ print_uint(PRINT_ANY, "other", "other %u ", qopt->other);
+ }
+
+ if (!is_json_context())
+ printf("\n Total packets: ");
+
+ print_uint(PRINT_ANY, "packets", "%u ", qopt->packets);
+
+ print_uint(PRINT_JSON, "bytes", NULL, bytes);
+ print_string(PRINT_FP, NULL, "(%s) ", sprint_size(bytes, b1));
+}
+
static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
{
+ struct tc_gred_info infos[MAX_DPs] = {};
struct rtattr *tb[TCA_GRED_MAX + 1];
struct tc_gred_sopt *sopt;
struct tc_gred_qopt *qopt;
+ bool vq_info = false;
__u32 *max_p = NULL;
__u32 *limit = NULL;
unsigned int i;
SPRINT_BUF(b1);
- SPRINT_BUF(b2);
- SPRINT_BUF(b3);
if (opt == NULL)
return 0;
return -1;
}
-/* Bad hack! should really return a proper message as shown above*/
+ if (tb[TCA_GRED_VQ_LIST]) {
+ gred_parse_vqs(infos, tb[TCA_GRED_VQ_LIST]);
+ vq_info = true;
+ }
- fprintf(f, "vqs %u default %u %s",
- sopt->DPs,
- sopt->def_DP,
- sopt->grio ? "grio " : "");
+ print_uint(PRINT_ANY, "dp_cnt", "vqs %u ", sopt->DPs);
+ print_uint(PRINT_ANY, "dp_default", "default %u ", sopt->def_DP);
- if (limit)
- fprintf(f, "limit %s ",
- sprint_size(*limit, b1));
+ if (sopt->grio)
+ print_bool(PRINT_ANY, "grio", "grio ", true);
+ else
+ print_bool(PRINT_ANY, "grio", NULL, false);
+ if (limit) {
+ print_uint(PRINT_JSON, "limit", NULL, *limit);
+ print_string(PRINT_FP, NULL, "limit %s ",
+ sprint_size(*limit, b1));
+ }
+
+ tc_red_print_flags(sopt->flags);
+
+ open_json_array(PRINT_JSON, "vqs");
for (i = 0; i < MAX_DPs; i++, qopt++) {
- if (qopt->DP >= MAX_DPs) continue;
- fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ",
- qopt->DP,
- qopt->prio,
- sprint_size(qopt->limit, b1),
- sprint_size(qopt->qth_min, b2),
- sprint_size(qopt->qth_max, b3));
+ if (qopt->DP >= MAX_DPs)
+ continue;
+
+ open_json_object(NULL);
+
+ print_uint(PRINT_ANY, "vq", "\n vq %u ", qopt->DP);
+ print_hhu(PRINT_ANY, "prio", "prio %hhu ", qopt->prio);
+
+ print_uint(PRINT_JSON, "limit", NULL, qopt->limit);
+ print_string(PRINT_FP, NULL, "limit %s ",
+ sprint_size(qopt->limit, b1));
+
+ print_uint(PRINT_JSON, "min", NULL, qopt->qth_min);
+ print_string(PRINT_FP, NULL, "min %s ",
+ sprint_size(qopt->qth_min, b1));
+
+ print_uint(PRINT_JSON, "max", NULL, qopt->qth_max);
+ print_string(PRINT_FP, NULL, "max %s ",
+ sprint_size(qopt->qth_max, b1));
+
+ if (infos[i].flags_present)
+ tc_red_print_flags(infos[i].flags);
+
if (show_details) {
- fprintf(f, "ewma %u ", qopt->Wlog);
+ print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog);
if (max_p)
- fprintf(f, "probability %lg ", max_p[i] / pow(2, 32));
+ print_float(PRINT_ANY, "probability",
+ "probability %lg ",
+ max_p[i] / pow(2, 32));
else
- fprintf(f, "Plog %u ", qopt->Plog);
- fprintf(f, "Scell_log %u ", qopt->Scell_log);
- }
- if (show_stats) {
- fprintf(f, "\n Queue size: average %s current %s ",
- sprint_size(qopt->qave, b1),
- sprint_size(qopt->backlog, b2));
- fprintf(f, "\n Dropped packets: forced %u early %u pdrop %u other %u ",
- qopt->forced,
- qopt->early,
- qopt->pdrop,
- qopt->other);
- fprintf(f, "\n Total packets: %u (%s) ",
- qopt->packets,
- sprint_size(qopt->bytesin, b1));
+ print_uint(PRINT_ANY, "Plog", "Plog %u ",
+ qopt->Plog);
+ print_uint(PRINT_ANY, "Scell_log", "Scell_log %u ",
+ qopt->Scell_log);
}
+ if (show_stats)
+ gred_print_stats(vq_info ? &infos[i] : NULL, qopt);
+ close_json_object();
}
+ close_json_array(PRINT_JSON, "vqs");
return 0;
}
print_uint(PRINT_JSON, "max", NULL, qopt->qth_max);
print_string(PRINT_FP, NULL, "max %s ", sprint_size(qopt->qth_max, b3));
- if (qopt->flags & TC_RED_ECN)
- print_bool(PRINT_ANY, "ecn", "ecn ", true);
- else
- print_bool(PRINT_ANY, "ecn", NULL, false);
- if (qopt->flags & TC_RED_HARDDROP)
- print_bool(PRINT_ANY, "harddrop", "harddrop ", true);
- else
- print_bool(PRINT_ANY, "harddrop", NULL, false);
- if (qopt->flags & TC_RED_ADAPTATIVE)
- print_bool(PRINT_ANY, "adaptive", "adaptive ", true);
- else
- print_bool(PRINT_ANY, "adaptive", NULL, false);
+ tc_red_print_flags(qopt->flags);
+
if (show_details) {
print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog);
if (max_P)
sprint_size(qopt_ext->qth_min, b2),
sprint_size(qopt_ext->qth_max, b3),
qopt_ext->max_P / pow(2, 32));
- if (qopt_ext->flags & TC_RED_ECN)
- fprintf(f, "ecn ");
+ tc_red_print_flags(qopt_ext->flags);
if (show_stats) {
fprintf(f, "\n prob_mark %u prob_mark_head %u prob_drop %u",
qopt_ext->stats.prob_mark,
#include <arpa/inet.h>
#include <string.h>
+#include "utils.h"
#include "tc_core.h"
+#include "tc_util.h"
#include "tc_red.h"
/*
sbuf[255] = 31;
return clog;
}
+
+void tc_red_print_flags(__u32 flags)
+{
+ if (flags & TC_RED_ECN)
+ print_bool(PRINT_ANY, "ecn", "ecn ", true);
+ else
+ print_bool(PRINT_ANY, "ecn", NULL, false);
+
+ if (flags & TC_RED_HARDDROP)
+ print_bool(PRINT_ANY, "harddrop", "harddrop ", true);
+ else
+ print_bool(PRINT_ANY, "harddrop", NULL, false);
+
+ if (flags & TC_RED_ADAPTATIVE)
+ print_bool(PRINT_ANY, "adaptive", "adaptive ", true);
+ else
+ print_bool(PRINT_ANY, "adaptive", NULL, false);
+}
int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt);
int tc_red_eval_idle_damping(int wlog, unsigned avpkt, unsigned bandwidth,
__u8 *sbuf);
+void tc_red_print_flags(__u32 flags);
#endif