diff --git a/devlink/devlink.c b/devlink/devlink.c index ccca0fb3..23db9e7c 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -28,6 +28,10 @@ #define ESWITCH_MODE_LEGACY "legacy" #define ESWITCH_MODE_SWITCHDEV "switchdev" +#define ESWITCH_INLINE_MODE_NONE "none" +#define ESWITCH_INLINE_MODE_LINK "link" +#define ESWITCH_INLINE_MODE_NETWORK "network" +#define ESWITCH_INLINE_MODE_TRANSPORT "transport" #define pr_err(args...) fprintf(stderr, ##args) #define pr_out(args...) fprintf(stdout, ##args) @@ -132,6 +136,7 @@ static void ifname_map_free(struct ifname_map *ifname_map) #define DL_OPT_SB_TH BIT(9) #define DL_OPT_SB_TC BIT(10) #define DL_OPT_ESWITCH_MODE BIT(11) +#define DL_OPT_ESWITCH_INLINE_MODE BIT(12) struct dl_opts { uint32_t present; /* flags of present items */ @@ -148,6 +153,7 @@ struct dl_opts { uint32_t sb_threshold; uint16_t sb_tc_index; enum devlink_eswitch_mode eswitch_mode; + enum devlink_eswitch_inline_mode eswitch_inline_mode; }; struct dl { @@ -305,6 +311,9 @@ static int attr_cb(const struct nlattr *attr, void *data) if (type == DEVLINK_ATTR_ESWITCH_MODE && mnl_attr_validate(attr, MNL_TYPE_U16) < 0) return MNL_CB_ERROR; + if (type == DEVLINK_ATTR_ESWITCH_INLINE_MODE && + mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; tb[type] = attr; return MNL_CB_OK; } @@ -682,6 +691,24 @@ static int eswitch_mode_get(const char *typestr, return 0; } +static int eswitch_inline_mode_get(const char *typestr, + enum devlink_eswitch_inline_mode *p_mode) +{ + if (strcmp(typestr, ESWITCH_INLINE_MODE_NONE) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NONE; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_LINK) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_LINK; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_NETWORK) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_NETWORK; + } else if (strcmp(typestr, ESWITCH_INLINE_MODE_TRANSPORT) == 0) { + *p_mode = DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT; + } else { + pr_err("Unknown eswitch inline mode \"%s\"\n", typestr); + return -EINVAL; + } + return 0; +} + static int dl_argv_parse(struct dl *dl, uint32_t o_required, uint32_t o_optional) { @@ -803,6 +830,19 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, if (err) return err; o_found |= DL_OPT_ESWITCH_MODE; + } else if (dl_argv_match(dl, "inline-mode") && + (o_all & DL_OPT_ESWITCH_INLINE_MODE)) { + const char *typestr; + + dl_arg_inc(dl); + err = dl_argv_str(dl, &typestr); + if (err) + return err; + err = eswitch_inline_mode_get( + typestr, &opts->eswitch_inline_mode); + if (err) + return err; + o_found |= DL_OPT_ESWITCH_INLINE_MODE; } else { pr_err("Unknown option \"%s\"\n", dl_argv(dl)); return -EINVAL; @@ -863,6 +903,12 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, return -EINVAL; } + if ((o_required & DL_OPT_ESWITCH_INLINE_MODE) && + !(o_found & DL_OPT_ESWITCH_INLINE_MODE)) { + pr_err("E-Switch inline-mode option expected.\n"); + return -EINVAL; + } + return 0; } @@ -909,6 +955,9 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) if (opts->present & DL_OPT_ESWITCH_MODE) mnl_attr_put_u16(nlh, DEVLINK_ATTR_ESWITCH_MODE, opts->eswitch_mode); + if (opts->present & DL_OPT_ESWITCH_INLINE_MODE) + mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_INLINE_MODE, + opts->eswitch_inline_mode); } static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, @@ -963,6 +1012,9 @@ static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) static void cmd_dev_help(void) { pr_err("Usage: devlink dev show [ DEV ]\n"); + pr_err(" devlink dev eswitch set DEV [ mode { legacy | switchdev } ]\n"); + pr_err(" [ inline-mode { none | link | network | transport } ]\n"); + pr_err(" devlink dev eswitch show DEV\n"); } static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name, @@ -1201,6 +1253,22 @@ static const char *eswitch_mode_name(uint32_t mode) } } +static const char *eswitch_inline_mode_name(uint32_t mode) +{ + switch (mode) { + case DEVLINK_ESWITCH_INLINE_MODE_NONE: + return ESWITCH_INLINE_MODE_NONE; + case DEVLINK_ESWITCH_INLINE_MODE_LINK: + return ESWITCH_INLINE_MODE_LINK; + case DEVLINK_ESWITCH_INLINE_MODE_NETWORK: + return ESWITCH_INLINE_MODE_NETWORK; + case DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: + return ESWITCH_INLINE_MODE_TRANSPORT; + default: + return ""; + } +} + static void pr_out_eswitch(struct dl *dl, struct nlattr **tb) { __pr_out_handle_start(dl, tb, true, false); @@ -1208,6 +1276,12 @@ static void pr_out_eswitch(struct dl *dl, struct nlattr **tb) if (tb[DEVLINK_ATTR_ESWITCH_MODE]) pr_out_str(dl, "mode", eswitch_mode_name(mnl_attr_get_u16(tb[DEVLINK_ATTR_ESWITCH_MODE]))); + + if (tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) + pr_out_str(dl, "inline-mode", + eswitch_inline_mode_name(mnl_attr_get_u8( + tb[DEVLINK_ATTR_ESWITCH_INLINE_MODE]))); + pr_out_handle_end(dl); } @@ -1250,16 +1324,27 @@ static int cmd_dev_eswitch_set(struct dl *dl) nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_ESWITCH_MODE_SET, NLM_F_REQUEST | NLM_F_ACK); - err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_ESWITCH_MODE, 0); + err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, + DL_OPT_ESWITCH_MODE | + DL_OPT_ESWITCH_INLINE_MODE); + if (err) return err; + if (dl->opts.present == 1) { + pr_err("Need to set at least one option\n"); + return -ENOENT; + } + return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL); } static int cmd_dev_eswitch(struct dl *dl) { - if (dl_argv_match(dl, "set")) { + if (dl_argv_match(dl, "help") || dl_no_arg(dl)) { + cmd_dev_help(); + return 0; + } else if (dl_argv_match(dl, "set")) { dl_arg_inc(dl); return cmd_dev_eswitch_set(dl); } else if (dl_argv_match(dl, "show")) { diff --git a/include/linux/sctp.h b/include/linux/sctp.h new file mode 100644 index 00000000..eee08c06 --- /dev/null +++ b/include/linux/sctp.h @@ -0,0 +1,1005 @@ +/* SCTP kernel implementation + * (C) Copyright IBM Corp. 2001, 2004 + * Copyright (c) 1999-2000 Cisco, Inc. + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2002 Intel Corp. + * + * This file is part of the SCTP kernel implementation + * + * This header represents the structures and constants needed to support + * the SCTP Extension to the Sockets API. + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * ************************ + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * La Monte H.P. Yarroll + * R. Stewart + * K. Morneau + * Q. Xie + * Karl Knutson + * Jon Grimm + * Daisy Chang + * Ryan Layer + * Ardelle Fan + * Sridhar Samudrala + * Inaky Perez-Gonzalez + * Vlad Yasevich + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +#ifndef _SCTP_H +#define _SCTP_H + +#include +#include + +typedef __s32 sctp_assoc_t; + +/* The following symbols come from the Sockets API Extensions for + * SCTP . + */ +#define SCTP_RTOINFO 0 +#define SCTP_ASSOCINFO 1 +#define SCTP_INITMSG 2 +#define SCTP_NODELAY 3 /* Get/set nodelay option. */ +#define SCTP_AUTOCLOSE 4 +#define SCTP_SET_PEER_PRIMARY_ADDR 5 +#define SCTP_PRIMARY_ADDR 6 +#define SCTP_ADAPTATION_LAYER 7 +#define SCTP_DISABLE_FRAGMENTS 8 +#define SCTP_PEER_ADDR_PARAMS 9 +#define SCTP_DEFAULT_SEND_PARAM 10 +#define SCTP_EVENTS 11 +#define SCTP_I_WANT_MAPPED_V4_ADDR 12 /* Turn on/off mapped v4 addresses */ +#define SCTP_MAXSEG 13 /* Get/set maximum fragment. */ +#define SCTP_STATUS 14 +#define SCTP_GET_PEER_ADDR_INFO 15 +#define SCTP_DELAYED_ACK_TIME 16 +#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME +#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME +#define SCTP_CONTEXT 17 +#define SCTP_FRAGMENT_INTERLEAVE 18 +#define SCTP_PARTIAL_DELIVERY_POINT 19 /* Set/Get partial delivery point */ +#define SCTP_MAX_BURST 20 /* Set/Get max burst */ +#define SCTP_AUTH_CHUNK 21 /* Set only: add a chunk type to authenticate */ +#define SCTP_HMAC_IDENT 22 +#define SCTP_AUTH_KEY 23 +#define SCTP_AUTH_ACTIVE_KEY 24 +#define SCTP_AUTH_DELETE_KEY 25 +#define SCTP_PEER_AUTH_CHUNKS 26 /* Read only */ +#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */ +#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ +#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ +#define SCTP_AUTO_ASCONF 30 +#define SCTP_PEER_ADDR_THLDS 31 +#define SCTP_RECVRCVINFO 32 +#define SCTP_RECVNXTINFO 33 +#define SCTP_DEFAULT_SNDINFO 34 + +/* Internal Socket Options. Some of the sctp library functions are + * implemented using these socket options. + */ +#define SCTP_SOCKOPT_BINDX_ADD 100 /* BINDX requests for adding addrs */ +#define SCTP_SOCKOPT_BINDX_REM 101 /* BINDX requests for removing addrs. */ +#define SCTP_SOCKOPT_PEELOFF 102 /* peel off association. */ +/* Options 104-106 are deprecated and removed. Do not use this space */ +#define SCTP_SOCKOPT_CONNECTX_OLD 107 /* CONNECTX old requests. */ +#define SCTP_GET_PEER_ADDRS 108 /* Get all peer address. */ +#define SCTP_GET_LOCAL_ADDRS 109 /* Get all local address. */ +#define SCTP_SOCKOPT_CONNECTX 110 /* CONNECTX requests. */ +#define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */ +#define SCTP_GET_ASSOC_STATS 112 /* Read only */ +#define SCTP_PR_SUPPORTED 113 +#define SCTP_DEFAULT_PRINFO 114 +#define SCTP_PR_ASSOC_STATUS 115 + +/* PR-SCTP policies */ +#define SCTP_PR_SCTP_NONE 0x0000 +#define SCTP_PR_SCTP_TTL 0x0010 +#define SCTP_PR_SCTP_RTX 0x0020 +#define SCTP_PR_SCTP_PRIO 0x0030 +#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_PRIO +#define SCTP_PR_SCTP_MASK 0x0030 + +#define __SCTP_PR_INDEX(x) ((x >> 4) - 1) +#define SCTP_PR_INDEX(x) __SCTP_PR_INDEX(SCTP_PR_SCTP_ ## x) + +#define SCTP_PR_POLICY(x) ((x) & SCTP_PR_SCTP_MASK) +#define SCTP_PR_SET_POLICY(flags, x) \ + do { \ + flags &= ~SCTP_PR_SCTP_MASK; \ + flags |= x; \ + } while (0) + +#define SCTP_PR_TTL_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_TTL) +#define SCTP_PR_RTX_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_RTX) +#define SCTP_PR_PRIO_ENABLED(x) (SCTP_PR_POLICY(x) == SCTP_PR_SCTP_PRIO) + +/* These are bit fields for msghdr->msg_flags. See section 5.1. */ +/* On user space Linux, these live in as an enum. */ +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + +/* 5.3.1 SCTP Initiation Structure (SCTP_INIT) + * + * This cmsghdr structure provides information for initializing new + * SCTP associations with sendmsg(). The SCTP_INITMSG socket option + * uses this same data structure. This structure is not used for + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg + */ +struct sctp_initmsg { + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + __u16 sinit_max_attempts; + __u16 sinit_max_init_timeo; +}; + +/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV) + * + * This cmsghdr structure specifies SCTP options for sendmsg() and + * describes SCTP header information about a received message through + * recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ---------------------- + * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo + */ +struct sctp_sndrcvinfo { + __u16 sinfo_stream; + __u16 sinfo_ssn; + __u16 sinfo_flags; + __u32 sinfo_ppid; + __u32 sinfo_context; + __u32 sinfo_timetolive; + __u32 sinfo_tsn; + __u32 sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + +/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo + */ +struct sctp_sndinfo { + __u16 snd_sid; + __u16 snd_flags; + __u32 snd_ppid; + __u32 snd_context; + sctp_assoc_t snd_assoc_id; +}; + +/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO) + * + * This cmsghdr structure describes SCTP receive information + * about a received message through recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_RCVINFO struct sctp_rcvinfo + */ +struct sctp_rcvinfo { + __u16 rcv_sid; + __u16 rcv_ssn; + __u16 rcv_flags; + __u32 rcv_ppid; + __u32 rcv_tsn; + __u32 rcv_cumtsn; + __u32 rcv_context; + sctp_assoc_t rcv_assoc_id; +}; + +/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO) + * + * This cmsghdr structure describes SCTP receive information + * of the next message that will be delivered through recvmsg() + * if this information is already available when delivering + * the current message. + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_NXTINFO struct sctp_nxtinfo + */ +struct sctp_nxtinfo { + __u16 nxt_sid; + __u16 nxt_flags; + __u32 nxt_ppid; + __u32 nxt_length; + sctp_assoc_t nxt_assoc_id; +}; + +/* + * sinfo_flags: 16 bits (unsigned integer) + * + * This field may contain any of the following flags and is composed of + * a bitwise OR of these values. + */ +enum sctp_sinfo_flags { + SCTP_UNORDERED = (1 << 0), /* Send/receive message unordered. */ + SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */ + SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */ + SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */ + SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */ + SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */ +}; + +typedef union { + __u8 raw; + struct sctp_initmsg init; + struct sctp_sndrcvinfo sndrcv; +} sctp_cmsg_data_t; + +/* These are cmsg_types. */ +typedef enum sctp_cmsg_type { + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ +#define SCTP_INIT SCTP_INIT + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ +#define SCTP_SNDRCV SCTP_SNDRCV + SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */ +#define SCTP_SNDINFO SCTP_SNDINFO + SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */ +#define SCTP_RCVINFO SCTP_RCVINFO + SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */ +#define SCTP_NXTINFO SCTP_NXTINFO +} sctp_cmsg_t; + +/* + * 5.3.1.1 SCTP_ASSOC_CHANGE + * + * Communication notifications inform the ULP that an SCTP association + * has either begun or ended. The identifier for a new association is + * provided by this notificaion. The notification information has the + * following format: + * + */ +struct sctp_assoc_change { + __u16 sac_type; + __u16 sac_flags; + __u32 sac_length; + __u16 sac_state; + __u16 sac_error; + __u16 sac_outbound_streams; + __u16 sac_inbound_streams; + sctp_assoc_t sac_assoc_id; + __u8 sac_info[0]; +}; + +/* + * sac_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the association. They include: + * + * Note: The following state names deviate from the API draft as + * the names clash too easily with other kernel symbols. + */ +enum sctp_sac_state { + SCTP_COMM_UP, + SCTP_COMM_LOST, + SCTP_RESTART, + SCTP_SHUTDOWN_COMP, + SCTP_CANT_STR_ASSOC, +}; + +/* + * 5.3.1.2 SCTP_PEER_ADDR_CHANGE + * + * When a destination address on a multi-homed peer encounters a change + * an interface details event is sent. The information has the + * following structure: + */ +struct sctp_paddr_change { + __u16 spc_type; + __u16 spc_flags; + __u32 spc_length; + struct sockaddr_storage spc_aaddr; + int spc_state; + int spc_error; + sctp_assoc_t spc_assoc_id; +} __attribute__((packed, aligned(4))); + +/* + * spc_state: 32 bits (signed integer) + * + * This field holds one of a number of values that communicate the + * event that happened to the address. They include: + */ +enum sctp_spc_state { + SCTP_ADDR_AVAILABLE, + SCTP_ADDR_UNREACHABLE, + SCTP_ADDR_REMOVED, + SCTP_ADDR_ADDED, + SCTP_ADDR_MADE_PRIM, + SCTP_ADDR_CONFIRMED, +}; + + +/* + * 5.3.1.3 SCTP_REMOTE_ERROR + * + * A remote peer may send an Operational Error message to its peer. + * This message indicates a variety of error conditions on an + * association. The entire error TLV as it appears on the wire is + * included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP + * specification [SCTP] and any extensions for a list of possible + * error formats. SCTP error TLVs have the format: + */ +struct sctp_remote_error { + __u16 sre_type; + __u16 sre_flags; + __u32 sre_length; + __u16 sre_error; + sctp_assoc_t sre_assoc_id; + __u8 sre_data[0]; +}; + + +/* + * 5.3.1.4 SCTP_SEND_FAILED + * + * If SCTP cannot deliver a message it may return the message as a + * notification. + */ +struct sctp_send_failed { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndrcvinfo ssf_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +/* + * ssf_flags: 16 bits (unsigned integer) + * + * The flag value will take one of the following values + * + * SCTP_DATA_UNSENT - Indicates that the data was never put on + * the wire. + * + * SCTP_DATA_SENT - Indicates that the data was put on the wire. + * Note that this does not necessarily mean that the + * data was (or was not) successfully delivered. + */ +enum sctp_ssf_flags { + SCTP_DATA_UNSENT, + SCTP_DATA_SENT, +}; + +/* + * 5.3.1.5 SCTP_SHUTDOWN_EVENT + * + * When a peer sends a SHUTDOWN, SCTP delivers this notification to + * inform the application that it should cease sending data. + */ +struct sctp_shutdown_event { + __u16 sse_type; + __u16 sse_flags; + __u32 sse_length; + sctp_assoc_t sse_assoc_id; +}; + +/* + * 5.3.1.6 SCTP_ADAPTATION_INDICATION + * + * When a peer sends a Adaptation Layer Indication parameter , SCTP + * delivers this notification to inform the application + * that of the peers requested adaptation layer. + */ +struct sctp_adaptation_event { + __u16 sai_type; + __u16 sai_flags; + __u32 sai_length; + __u32 sai_adaptation_ind; + sctp_assoc_t sai_assoc_id; +}; + +/* + * 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT + * + * When a receiver is engaged in a partial delivery of a + * message this notification will be used to indicate + * various events. + */ +struct sctp_pdapi_event { + __u16 pdapi_type; + __u16 pdapi_flags; + __u32 pdapi_length; + __u32 pdapi_indication; + sctp_assoc_t pdapi_assoc_id; +}; + +enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, }; + +/* + * 5.3.1.8. SCTP_AUTHENTICATION_EVENT + * + * When a receiver is using authentication this message will provide + * notifications regarding new keys being made active as well as errors. + */ +struct sctp_authkey_event { + __u16 auth_type; + __u16 auth_flags; + __u32 auth_length; + __u16 auth_keynumber; + __u16 auth_altkeynumber; + __u32 auth_indication; + sctp_assoc_t auth_assoc_id; +}; + +enum { SCTP_AUTH_NEWKEY = 0, }; + +/* + * 6.1.9. SCTP_SENDER_DRY_EVENT + * + * When the SCTP stack has no more user data to send or retransmit, this + * notification is given to the user. Also, at the time when a user app + * subscribes to this event, if there is no data to be sent or + * retransmit, the stack will immediately send up this notification. + */ +struct sctp_sender_dry_event { + __u16 sender_dry_type; + __u16 sender_dry_flags; + __u32 sender_dry_length; + sctp_assoc_t sender_dry_assoc_id; +}; + +/* + * Described in Section 7.3 + * Ancillary Data and Notification Interest Options + */ +struct sctp_event_subscribe { + __u8 sctp_data_io_event; + __u8 sctp_association_event; + __u8 sctp_address_event; + __u8 sctp_send_failure_event; + __u8 sctp_peer_error_event; + __u8 sctp_shutdown_event; + __u8 sctp_partial_delivery_event; + __u8 sctp_adaptation_layer_event; + __u8 sctp_authentication_event; + __u8 sctp_sender_dry_event; +}; + +/* + * 5.3.1 SCTP Notification Structure + * + * The notification structure is defined as the union of all + * notification types. + * + */ +union sctp_notification { + struct { + __u16 sn_type; /* Notification type. */ + __u16 sn_flags; + __u32 sn_length; + } sn_header; + struct sctp_assoc_change sn_assoc_change; + struct sctp_paddr_change sn_paddr_change; + struct sctp_remote_error sn_remote_error; + struct sctp_send_failed sn_send_failed; + struct sctp_shutdown_event sn_shutdown_event; + struct sctp_adaptation_event sn_adaptation_event; + struct sctp_pdapi_event sn_pdapi_event; + struct sctp_authkey_event sn_authkey_event; + struct sctp_sender_dry_event sn_sender_dry_event; +}; + +/* Section 5.3.1 + * All standard values for sn_type flags are greater than 2^15. + * Values from 2^15 and down are reserved. + */ + +enum sctp_sn_type { + SCTP_SN_TYPE_BASE = (1<<15), + SCTP_ASSOC_CHANGE, +#define SCTP_ASSOC_CHANGE SCTP_ASSOC_CHANGE + SCTP_PEER_ADDR_CHANGE, +#define SCTP_PEER_ADDR_CHANGE SCTP_PEER_ADDR_CHANGE + SCTP_SEND_FAILED, +#define SCTP_SEND_FAILED SCTP_SEND_FAILED + SCTP_REMOTE_ERROR, +#define SCTP_REMOTE_ERROR SCTP_REMOTE_ERROR + SCTP_SHUTDOWN_EVENT, +#define SCTP_SHUTDOWN_EVENT SCTP_SHUTDOWN_EVENT + SCTP_PARTIAL_DELIVERY_EVENT, +#define SCTP_PARTIAL_DELIVERY_EVENT SCTP_PARTIAL_DELIVERY_EVENT + SCTP_ADAPTATION_INDICATION, +#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTATION_INDICATION + SCTP_AUTHENTICATION_EVENT, +#define SCTP_AUTHENTICATION_INDICATION SCTP_AUTHENTICATION_EVENT + SCTP_SENDER_DRY_EVENT, +#define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT +}; + +/* Notification error codes used to fill up the error fields in some + * notifications. + * SCTP_PEER_ADDRESS_CHAGE : spc_error + * SCTP_ASSOC_CHANGE : sac_error + * These names should be potentially included in the draft 04 of the SCTP + * sockets API specification. + */ +typedef enum sctp_sn_error { + SCTP_FAILED_THRESHOLD, + SCTP_RECEIVED_SACK, + SCTP_HEARTBEAT_SUCCESS, + SCTP_RESPONSE_TO_USER_REQ, + SCTP_INTERNAL_ERROR, + SCTP_SHUTDOWN_GUARD_EXPIRES, + SCTP_PEER_FAULTY, +} sctp_sn_error_t; + +/* + * 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO) + * + * The protocol parameters used to initialize and bound retransmission + * timeout (RTO) are tunable. See [SCTP] for more information on how + * these parameters are used in RTO calculation. + */ +struct sctp_rtoinfo { + sctp_assoc_t srto_assoc_id; + __u32 srto_initial; + __u32 srto_max; + __u32 srto_min; +}; + +/* + * 7.1.2 Association Parameters (SCTP_ASSOCINFO) + * + * This option is used to both examine and set various association and + * endpoint parameters. + */ +struct sctp_assocparams { + sctp_assoc_t sasoc_assoc_id; + __u16 sasoc_asocmaxrxt; + __u16 sasoc_number_peer_destinations; + __u32 sasoc_peer_rwnd; + __u32 sasoc_local_rwnd; + __u32 sasoc_cookie_life; +}; + +/* + * 7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR) + * + * Requests that the peer mark the enclosed address as the association + * primary. The enclosed address must be one of the association's + * locally bound addresses. The following structure is used to make a + * set primary request: + */ +struct sctp_setpeerprim { + sctp_assoc_t sspp_assoc_id; + struct sockaddr_storage sspp_addr; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) + * + * Requests that the local SCTP stack use the enclosed peer address as + * the association primary. The enclosed address must be one of the + * association peer's addresses. The following structure is used to + * make a set peer primary request: + */ +struct sctp_prim { + sctp_assoc_t ssp_assoc_id; + struct sockaddr_storage ssp_addr; +} __attribute__((packed, aligned(4))); + +/* For backward compatibility use, define the old name too */ +#define sctp_setprim sctp_prim + +/* + * 7.1.11 Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER) + * + * Requests that the local endpoint set the specified Adaptation Layer + * Indication parameter for all future INIT and INIT-ACK exchanges. + */ +struct sctp_setadaptation { + __u32 ssb_adaptation_ind; +}; + +/* + * 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) + * + * Applications can enable or disable heartbeats for any peer address + * of an association, modify an address's heartbeat interval, force a + * heartbeat to be sent immediately, and adjust the address's maximum + * number of retransmissions sent before an address is considered + * unreachable. The following structure is used to access and modify an + * address's parameters: + */ +enum sctp_spp_flags { + SPP_HB_ENABLE = 1<<0, /*Enable heartbeats*/ + SPP_HB_DISABLE = 1<<1, /*Disable heartbeats*/ + SPP_HB = SPP_HB_ENABLE | SPP_HB_DISABLE, + SPP_HB_DEMAND = 1<<2, /*Send heartbeat immediately*/ + SPP_PMTUD_ENABLE = 1<<3, /*Enable PMTU discovery*/ + SPP_PMTUD_DISABLE = 1<<4, /*Disable PMTU discovery*/ + SPP_PMTUD = SPP_PMTUD_ENABLE | SPP_PMTUD_DISABLE, + SPP_SACKDELAY_ENABLE = 1<<5, /*Enable SACK*/ + SPP_SACKDELAY_DISABLE = 1<<6, /*Disable SACK*/ + SPP_SACKDELAY = SPP_SACKDELAY_ENABLE | SPP_SACKDELAY_DISABLE, + SPP_HB_TIME_IS_ZERO = 1<<7, /* Set HB delay to 0 */ +}; + +struct sctp_paddrparams { + sctp_assoc_t spp_assoc_id; + struct sockaddr_storage spp_address; + __u32 spp_hbinterval; + __u16 spp_pathmaxrxt; + __u32 spp_pathmtu; + __u32 spp_sackdelay; + __u32 spp_flags; +} __attribute__((packed, aligned(4))); + +/* + * 7.1.18. Add a chunk that must be authenticated (SCTP_AUTH_CHUNK) + * + * This set option adds a chunk type that the user is requesting to be + * received only in an authenticated way. Changes to the list of chunks + * will only effect future associations on the socket. + */ +struct sctp_authchunk { + __u8 sauth_chunk; +}; + +/* + * 7.1.19. Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT) + * + * This option gets or sets the list of HMAC algorithms that the local + * endpoint requires the peer to use. + */ +/* This here is only used by user space as is. It might not be a good idea + * to export/reveal the whole structure with reserved fields etc. + */ +enum { + SCTP_AUTH_HMAC_ID_SHA1 = 1, + SCTP_AUTH_HMAC_ID_SHA256 = 3, +}; + +struct sctp_hmacalgo { + __u32 shmac_num_idents; + __u16 shmac_idents[]; +}; + +/* Sadly, user and kernel space have different names for + * this structure member, so this is to not break anything. + */ +#define shmac_number_of_idents shmac_num_idents + +/* + * 7.1.20. Set a shared key (SCTP_AUTH_KEY) + * + * This option will set a shared secret key which is used to build an + * association shared key. + */ +struct sctp_authkey { + sctp_assoc_t sca_assoc_id; + __u16 sca_keynumber; + __u16 sca_keylength; + __u8 sca_key[]; +}; + +/* + * 7.1.21. Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY) + * + * This option will get or set the active shared key to be used to build + * the association shared key. + */ + +struct sctp_authkeyid { + sctp_assoc_t scact_assoc_id; + __u16 scact_keynumber; +}; + + +/* + * 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK) + * + * This option will effect the way delayed acks are performed. This + * option allows you to get or set the delayed ack time, in + * milliseconds. It also allows changing the delayed ack frequency. + * Changing the frequency to 1 disables the delayed sack algorithm. If + * the assoc_id is 0, then this sets or gets the endpoints default + * values. If the assoc_id field is non-zero, then the set or get + * effects the specified association for the one to many model (the + * assoc_id field is ignored by the one to one model). Note that if + * sack_delay or sack_freq are 0 when setting this option, then the + * current values will remain unchanged. + */ +struct sctp_sack_info { + sctp_assoc_t sack_assoc_id; + uint32_t sack_delay; + uint32_t sack_freq; +}; + +struct sctp_assoc_value { + sctp_assoc_t assoc_id; + uint32_t assoc_value; +}; + +/* + * 7.2.2 Peer Address Information + * + * Applications can retrieve information about a specific peer address + * of an association, including its reachability state, congestion + * window, and retransmission timer values. This information is + * read-only. The following structure is used to access this + * information: + */ +struct sctp_paddrinfo { + sctp_assoc_t spinfo_assoc_id; + struct sockaddr_storage spinfo_address; + __s32 spinfo_state; + __u32 spinfo_cwnd; + __u32 spinfo_srtt; + __u32 spinfo_rto; + __u32 spinfo_mtu; +} __attribute__((packed, aligned(4))); + +/* Peer addresses's state. */ +/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x] + * calls. + * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters. + * Not yet confirmed by a heartbeat and not available for data + * transfers. + * ACTIVE : Peer address confirmed, active and available for data transfers. + * INACTIVE: Peer address inactive and not available for data transfers. + */ +enum sctp_spinfo_state { + SCTP_INACTIVE, + SCTP_PF, + SCTP_ACTIVE, + SCTP_UNCONFIRMED, + SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ +}; + +/* + * 7.2.1 Association Status (SCTP_STATUS) + * + * Applications can retrieve current status information about an + * association, including association state, peer receiver window size, + * number of unacked data chunks, and number of data chunks pending + * receipt. This information is read-only. The following structure is + * used to access this information: + */ +struct sctp_status { + sctp_assoc_t sstat_assoc_id; + __s32 sstat_state; + __u32 sstat_rwnd; + __u16 sstat_unackdata; + __u16 sstat_penddata; + __u16 sstat_instrms; + __u16 sstat_outstrms; + __u32 sstat_fragmentation_point; + struct sctp_paddrinfo sstat_primary; +}; + +/* + * 7.2.3. Get the list of chunks the peer requires to be authenticated + * (SCTP_PEER_AUTH_CHUNKS) + * + * This option gets a list of chunks for a specified association that + * the peer requires to be received authenticated only. + */ +struct sctp_authchunks { + sctp_assoc_t gauth_assoc_id; + __u32 gauth_number_of_chunks; + uint8_t gauth_chunks[]; +}; + +/* The broken spelling has been released already in lksctp-tools header, + * so don't break anyone, now that it's fixed. + */ +#define guth_number_of_chunks gauth_number_of_chunks + +/* Association states. */ +enum sctp_sstat_state { + SCTP_EMPTY = 0, + SCTP_CLOSED = 1, + SCTP_COOKIE_WAIT = 2, + SCTP_COOKIE_ECHOED = 3, + SCTP_ESTABLISHED = 4, + SCTP_SHUTDOWN_PENDING = 5, + SCTP_SHUTDOWN_SENT = 6, + SCTP_SHUTDOWN_RECEIVED = 7, + SCTP_SHUTDOWN_ACK_SENT = 8, +}; + +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +struct sctp_assoc_ids { + __u32 gaids_number_of_ids; + sctp_assoc_t gaids_assoc_id[]; +}; + +/* + * 8.3, 8.5 get all peer/local addresses in an association. + * This parameter struct is used by SCTP_GET_PEER_ADDRS and + * SCTP_GET_LOCAL_ADDRS socket options used internally to implement + * sctp_getpaddrs() and sctp_getladdrs() API. + */ +struct sctp_getaddrs_old { + sctp_assoc_t assoc_id; + int addr_num; + struct sockaddr *addrs; +}; + +struct sctp_getaddrs { + sctp_assoc_t assoc_id; /*input*/ + __u32 addr_num; /*output*/ + __u8 addrs[0]; /*output, variable size*/ +}; + +/* A socket user request obtained via SCTP_GET_ASSOC_STATS that retrieves + * association stats. All stats are counts except sas_maxrto and + * sas_obs_rto_ipaddr. maxrto is the max observed rto + transport since + * the last call. Will return 0 when RTO was not update since last call + */ +struct sctp_assoc_stats { + sctp_assoc_t sas_assoc_id; /* Input */ + /* Transport of observed max RTO */ + struct sockaddr_storage sas_obs_rto_ipaddr; + __u64 sas_maxrto; /* Maximum Observed RTO for period */ + __u64 sas_isacks; /* SACKs received */ + __u64 sas_osacks; /* SACKs sent */ + __u64 sas_opackets; /* Packets sent */ + __u64 sas_ipackets; /* Packets received */ + __u64 sas_rtxchunks; /* Retransmitted Chunks */ + __u64 sas_outofseqtsns;/* TSN received > next expected */ + __u64 sas_idupchunks; /* Dups received (ordered+unordered) */ + __u64 sas_gapcnt; /* Gap Acknowledgements Received */ + __u64 sas_ouodchunks; /* Unordered data chunks sent */ + __u64 sas_iuodchunks; /* Unordered data chunks received */ + __u64 sas_oodchunks; /* Ordered data chunks sent */ + __u64 sas_iodchunks; /* Ordered data chunks received */ + __u64 sas_octrlchunks; /* Control chunks sent */ + __u64 sas_ictrlchunks; /* Control chunks received */ +}; + +/* + * 8.1 sctp_bindx() + * + * The flags parameter is formed from the bitwise OR of zero or more of the + * following currently defined flags: + */ +#define SCTP_BINDX_ADD_ADDR 0x01 +#define SCTP_BINDX_REM_ADDR 0x02 + +/* This is the structure that is passed as an argument(optval) to + * getsockopt(SCTP_SOCKOPT_PEELOFF). + */ +typedef struct { + sctp_assoc_t associd; + int sd; +} sctp_peeloff_arg_t; + +/* + * Peer Address Thresholds socket option + */ +struct sctp_paddrthlds { + sctp_assoc_t spt_assoc_id; + struct sockaddr_storage spt_address; + __u16 spt_pathmaxrxt; + __u16 spt_pathpfthld; +}; + +/* + * Socket Option for Getting the Association/Stream-Specific PR-SCTP Status + */ +struct sctp_prstatus { + sctp_assoc_t sprstat_assoc_id; + __u16 sprstat_sid; + __u16 sprstat_policy; + __u64 sprstat_abandoned_unsent; + __u64 sprstat_abandoned_sent; +}; + +struct sctp_default_prinfo { + sctp_assoc_t pr_assoc_id; + __u32 pr_value; + __u16 pr_policy; +}; + +struct sctp_info { + __u32 sctpi_tag; + __u32 sctpi_state; + __u32 sctpi_rwnd; + __u16 sctpi_unackdata; + __u16 sctpi_penddata; + __u16 sctpi_instrms; + __u16 sctpi_outstrms; + __u32 sctpi_fragmentation_point; + __u32 sctpi_inqueue; + __u32 sctpi_outqueue; + __u32 sctpi_overall_error; + __u32 sctpi_max_burst; + __u32 sctpi_maxseg; + __u32 sctpi_peer_rwnd; + __u32 sctpi_peer_tag; + __u8 sctpi_peer_capable; + __u8 sctpi_peer_sack; + __u16 __reserved1; + + /* assoc status info */ + __u64 sctpi_isacks; + __u64 sctpi_osacks; + __u64 sctpi_opackets; + __u64 sctpi_ipackets; + __u64 sctpi_rtxchunks; + __u64 sctpi_outofseqtsns; + __u64 sctpi_idupchunks; + __u64 sctpi_gapcnt; + __u64 sctpi_ouodchunks; + __u64 sctpi_iuodchunks; + __u64 sctpi_oodchunks; + __u64 sctpi_iodchunks; + __u64 sctpi_octrlchunks; + __u64 sctpi_ictrlchunks; + + /* primary transport info */ + struct sockaddr_storage sctpi_p_address; + __s32 sctpi_p_state; + __u32 sctpi_p_cwnd; + __u32 sctpi_p_srtt; + __u32 sctpi_p_rto; + __u32 sctpi_p_hbinterval; + __u32 sctpi_p_pathmaxrxt; + __u32 sctpi_p_sackdelay; + __u32 sctpi_p_sackfreq; + __u32 sctpi_p_ssthresh; + __u32 sctpi_p_partial_bytes_acked; + __u32 sctpi_p_flight_size; + __u16 sctpi_p_error; + __u16 __reserved2; + + /* sctp sock info */ + __u32 sctpi_s_autoclose; + __u32 sctpi_s_adaptation_ind; + __u32 sctpi_s_pd_point; + __u8 sctpi_s_nodelay; + __u8 sctpi_s_disable_fragments; + __u8 sctpi_s_v4mapped; + __u8 sctpi_s_frag_interleave; + __u32 sctpi_s_type; + __u32 __reserved3; +}; + +#endif /* _SCTP_H */ diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 7f05258f..50897e6c 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -148,11 +148,11 @@ static void print_operstate(FILE *f, __u8 state) fprintf(f, "state %#x ", state); } else if (brief) { color_fprintf(f, oper_state_color(state), - "%-14s ", oper_states[state]); + "%-14s ", oper_states[state]); } else { fprintf(f, "state "); color_fprintf(f, oper_state_color(state), - "%s ", oper_states[state]); + "%s ", oper_states[state]); } } @@ -322,10 +322,7 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) { struct ifla_vf_mac *vf_mac; struct ifla_vf_tx_rate *vf_tx_rate; - struct ifla_vf_spoofchk *vf_spoofchk; - struct ifla_vf_link_state *vf_linkstate; struct rtattr *vf[IFLA_VF_MAX + 1] = {}; - struct rtattr *tmp; SPRINT_BUF(b1); @@ -339,31 +336,6 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) vf_mac = RTA_DATA(vf[IFLA_VF_MAC]); vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]); - /* Check if the spoof checking vf info type is supported by - * this kernel. - */ - tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] + - vf[IFLA_VF_TX_RATE]->rta_len); - - if (tmp->rta_type != IFLA_VF_SPOOFCHK) - vf_spoofchk = NULL; - else - vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]); - - if (vf_spoofchk) { - /* Check if the link state vf info type is supported by - * this kernel. - */ - tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] + - vf[IFLA_VF_SPOOFCHK]->rta_len); - - if (tmp->rta_type != IFLA_VF_LINK_STATE) - vf_linkstate = NULL; - else - vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]); - } else - vf_linkstate = NULL; - fprintf(fp, "%s vf %d MAC %s", _SL_, vf_mac->vf, ll_addr_n2a((unsigned char *)&vf_mac->mac, ETH_ALEN, 0, b1, sizeof(b1))); @@ -407,14 +379,18 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) if (vf_rate->min_tx_rate) fprintf(fp, ", min_tx_rate %dMbps", vf_rate->min_tx_rate); } + if (vf[IFLA_VF_SPOOFCHK]) { + struct ifla_vf_spoofchk *vf_spoofchk = + RTA_DATA(vf[IFLA_VF_SPOOFCHK]); - if (vf_spoofchk && vf_spoofchk->setting != -1) { - if (vf_spoofchk->setting) - fprintf(fp, ", spoof checking on"); - else - fprintf(fp, ", spoof checking off"); + if (vf_spoofchk->setting != -1) + fprintf(fp, ", spoof checking %s", + vf_spoofchk->setting ? "on" : "off"); } - if (vf_linkstate) { + if (vf[IFLA_VF_LINK_STATE]) { + struct ifla_vf_link_state *vf_linkstate = + RTA_DATA(vf[IFLA_VF_LINK_STATE]); + if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO) fprintf(fp, ", link-state auto"); else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE) @@ -427,7 +403,23 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) if (vf_trust->setting != -1) fprintf(fp, ", trust %s", - vf_trust->setting ? "on" : "off"); + vf_trust->setting ? "on" : "off"); + } + if (vf[IFLA_VF_RSS_QUERY_EN]) { + struct ifla_vf_rss_query_en *rss_query = + RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]); + + if (rss_query->setting != -1) + fprintf(fp, ", query_rss %s", + rss_query->setting ? "on" : "off"); + } + if (vf[IFLA_VF_RSS_QUERY_EN]) { + struct ifla_vf_rss_query_en *rss_query = + RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]); + + if (rss_query->setting != -1) + fprintf(fp, ", query_rss %s", + rss_query->setting ? "on" : "off"); } if (vf[IFLA_VF_STATS] && show_stats) print_vf_stats64(fp, vf[IFLA_VF_STATS]); @@ -448,7 +440,8 @@ static void print_num(FILE *fp, unsigned int width, uint64_t count) } /* increase value by a factor of 1000/1024 and print - * if result is something a human can read */ + * if result is something a human can read + */ for (;;) { powi *= base; if (count / base < powi) @@ -691,9 +684,9 @@ int print_linkinfo_brief(const struct sockaddr_nl *who, return -1; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - if (tb[IFLA_IFNAME] == NULL) { + if (tb[IFLA_IFNAME] == NULL) fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index); - } + if (filter.label && (!filter.family || filter.family == AF_PACKET) && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) @@ -787,9 +780,9 @@ int print_linkinfo(const struct sockaddr_nl *who, return 0; parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - if (tb[IFLA_IFNAME] == NULL) { + if (tb[IFLA_IFNAME] == NULL) fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index); - } + if (filter.label && (!filter.family || filter.family == AF_PACKET) && fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) @@ -1118,16 +1111,16 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, if (rta_tb[IFA_LOCAL]) { color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s", - format_host_rta(ifa->ifa_family, - rta_tb[IFA_LOCAL])); + format_host_rta(ifa->ifa_family, + rta_tb[IFA_LOCAL])); if (rta_tb[IFA_ADDRESS] && memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), - RTA_DATA(rta_tb[IFA_LOCAL]), - ifa->ifa_family == AF_INET ? 4 : 16)) { + RTA_DATA(rta_tb[IFA_LOCAL]), + ifa->ifa_family == AF_INET ? 4 : 16)) { fprintf(fp, " peer "); color_fprintf(fp, ifa_family_color(ifa->ifa_family), - "%s", format_host_rta(ifa->ifa_family, - rta_tb[IFA_ADDRESS])); + "%s", format_host_rta(ifa->ifa_family, + rta_tb[IFA_ADDRESS])); } fprintf(fp, "/%d ", ifa->ifa_prefixlen); } @@ -1138,14 +1131,14 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, if (rta_tb[IFA_BROADCAST]) { fprintf(fp, "brd "); color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ", - format_host_rta(ifa->ifa_family, - rta_tb[IFA_BROADCAST])); + format_host_rta(ifa->ifa_family, + rta_tb[IFA_BROADCAST])); } if (rta_tb[IFA_ANYCAST]) { fprintf(fp, "any "); color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ", - format_host_rta(ifa->ifa_family, - rta_tb[IFA_ANYCAST])); + format_host_rta(ifa->ifa_family, + rta_tb[IFA_ANYCAST])); } fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); if (ifa_flags & IFA_F_SECONDARY) { @@ -1184,9 +1177,9 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, ifa_flags &= ~IFA_F_MCAUTOJOIN; fprintf(fp, "autojoin "); } - if (!(ifa_flags & IFA_F_PERMANENT)) { + if (!(ifa_flags & IFA_F_PERMANENT)) fprintf(fp, "dynamic "); - } else + else ifa_flags &= ~IFA_F_PERMANENT; if (ifa_flags & IFA_F_DADFAILED) { ifa_flags &= ~IFA_F_DADFAILED; @@ -1672,9 +1665,9 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) filter.kind = *argv; } } else { - if (strcmp(*argv, "dev") == 0) { + if (strcmp(*argv, "dev") == 0) NEXT_ARG(); - } else if (matches(*argv, "help") == 0) + else if (matches(*argv, "help") == 0) usage(); if (filter_dev) duparg2("dev", *argv); @@ -1979,9 +1972,8 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv) } else if (strcmp(*argv, "autojoin") == 0) { ifa_flags |= IFA_F_MCAUTOJOIN; } else { - if (strcmp(*argv, "local") == 0) { + if (strcmp(*argv, "local") == 0) NEXT_ARG(); - } if (matches(*argv, "help") == 0) usage(); if (local_len) @@ -2012,9 +2004,9 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv) if (peer_len == 0 && local_len) { if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) { fprintf(stderr, - "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" \ - " Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" \ - " This special behaviour is likely to disappear in further releases,\n" \ + "Warning: Executing wildcard deletion to stay compatible with old scripts.\n" + " Explicitly specify the prefix length (%s/%d) to avoid this warning.\n" + " This special behaviour is likely to disappear in further releases,\n" " fix your scripts!\n", lcl_arg, local_len*8); } else { peer = lcl; diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c index d3338ace..0f91aebf 100644 --- a/ip/ipl2tp.c +++ b/ip/ipl2tp.c @@ -56,15 +56,15 @@ struct l2tp_parm { uint16_t pw_type; uint16_t mtu; - int udp6_csum_tx:1; - int udp6_csum_rx:1; - int udp_csum:1; - int recv_seq:1; - int send_seq:1; - int lns_mode:1; - int data_seq:2; - int tunnel:1; - int session:1; + unsigned int udp6_csum_tx:1; + unsigned int udp6_csum_rx:1; + unsigned int udp_csum:1; + unsigned int recv_seq:1; + unsigned int send_seq:1; + unsigned int lns_mode:1; + unsigned int data_seq:2; + unsigned int tunnel:1; + unsigned int session:1; int reorder_timeout; const char *ifname; uint8_t l2spec_type; @@ -110,17 +110,19 @@ static int create_tunnel(struct l2tp_parm *p) if (p->local_ip.family == AF_INET6) local_attr = L2TP_ATTR_IP6_SADDR; - addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, p->local_ip.bytelen); + addattr_l(&req.n, 1024, local_attr, &p->local_ip.data, + p->local_ip.bytelen); if (p->peer_ip.family == AF_INET6) peer_attr = L2TP_ATTR_IP6_DADDR; - addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, p->peer_ip.bytelen); + addattr_l(&req.n, 1024, peer_attr, &p->peer_ip.data, + p->peer_ip.bytelen); if (p->encap == L2TP_ENCAPTYPE_UDP) { addattr16(&req.n, 1024, L2TP_ATTR_UDP_SPORT, p->local_udp_port); addattr16(&req.n, 1024, L2TP_ATTR_UDP_DPORT, p->peer_udp_port); if (p->udp_csum) - addattr(&req.n, 1024, L2TP_ATTR_UDP_CSUM); + addattr8(&req.n, 1024, L2TP_ATTR_UDP_CSUM, 1); if (!p->udp6_csum_tx) addattr(&req.n, 1024, L2TP_ATTR_UDP_ZERO_CSUM6_TX); if (!p->udp6_csum_rx) @@ -159,18 +161,27 @@ static int create_session(struct l2tp_parm *p) addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_TYPE, p->l2spec_type); addattr8(&req.n, 1024, L2TP_ATTR_L2SPEC_LEN, p->l2spec_len); - if (p->mtu) addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu); - if (p->recv_seq) addattr(&req.n, 1024, L2TP_ATTR_RECV_SEQ); - if (p->send_seq) addattr(&req.n, 1024, L2TP_ATTR_SEND_SEQ); - if (p->lns_mode) addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE); - if (p->data_seq) addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq); - if (p->reorder_timeout) addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT, + if (p->mtu) + addattr16(&req.n, 1024, L2TP_ATTR_MTU, p->mtu); + if (p->recv_seq) + addattr8(&req.n, 1024, L2TP_ATTR_RECV_SEQ, 1); + if (p->send_seq) + addattr8(&req.n, 1024, L2TP_ATTR_SEND_SEQ, 1); + if (p->lns_mode) + addattr(&req.n, 1024, L2TP_ATTR_LNS_MODE); + if (p->data_seq) + addattr8(&req.n, 1024, L2TP_ATTR_DATA_SEQ, p->data_seq); + if (p->reorder_timeout) + addattr64(&req.n, 1024, L2TP_ATTR_RECV_TIMEOUT, p->reorder_timeout); - if (p->offset) addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset); - if (p->cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE, - p->cookie, p->cookie_len); - if (p->peer_cookie_len) addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE, - p->peer_cookie, p->peer_cookie_len); + if (p->offset) + addattr16(&req.n, 1024, L2TP_ATTR_OFFSET, p->offset); + if (p->cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_COOKIE, + p->cookie, p->cookie_len); + if (p->peer_cookie_len) + addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE, + p->peer_cookie, p->peer_cookie_len); if (p->ifname && p->ifname[0]) addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname); @@ -213,14 +224,37 @@ static void print_tunnel(const struct l2tp_data *data) p->tunnel_id, p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" : p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??"); - printf(" From %s ", inet_ntop(p->local_ip.family, p->local_ip.data, buf, sizeof(buf))); - printf("to %s\n", inet_ntop(p->peer_ip.family, p->peer_ip.data, buf, sizeof(buf))); + printf(" From %s ", + inet_ntop(p->local_ip.family, p->local_ip.data, + buf, sizeof(buf))); + printf("to %s\n", + inet_ntop(p->peer_ip.family, p->peer_ip.data, + buf, sizeof(buf))); printf(" Peer tunnel %u\n", p->peer_tunnel_id); - if (p->encap == L2TP_ENCAPTYPE_UDP) + if (p->encap == L2TP_ENCAPTYPE_UDP) { printf(" UDP source / dest ports: %hu/%hu\n", p->local_udp_port, p->peer_udp_port); + + switch (p->local_ip.family) { + case AF_INET: + printf(" UDP checksum: %s\n", + p->udp_csum ? "enabled" : "disabled"); + break; + case AF_INET6: + printf(" UDP checksum: %s%s%s%s\n", + p->udp6_csum_tx && p->udp6_csum_rx + ? "enabled" : "", + p->udp6_csum_tx && !p->udp6_csum_rx + ? "tx" : "", + !p->udp6_csum_tx && p->udp6_csum_rx + ? "rx" : "", + !p->udp6_csum_tx && !p->udp6_csum_rx + ? "disabled" : ""); + break; + } + } } static void print_session(struct l2tp_data *data) @@ -232,9 +266,9 @@ static void print_session(struct l2tp_data *data) printf(" Peer session %u, tunnel %u\n", p->peer_session_id, p->peer_tunnel_id); - if (p->ifname != NULL) { + if (p->ifname != NULL) printf(" interface name: %s\n", p->ifname); - } + printf(" offset %u, peer offset %u\n", p->offset, p->peer_offset); if (p->cookie_len > 0) @@ -246,6 +280,14 @@ static void print_session(struct l2tp_data *data) printf(" reorder timeout: %u\n", p->reorder_timeout); else printf("\n"); + if (p->send_seq || p->recv_seq) { + printf(" sequence numbering:"); + if (p->send_seq) + printf(" send"); + if (p->recv_seq) + printf(" recv"); + printf("\n"); + } } static int get_response(struct nlmsghdr *n, void *arg) @@ -289,13 +331,12 @@ static int get_response(struct nlmsghdr *n, void *arg) if (attrs[L2TP_ATTR_L2SPEC_LEN]) p->l2spec_len = rta_getattr_u8(attrs[L2TP_ATTR_L2SPEC_LEN]); - p->udp_csum = !!attrs[L2TP_ATTR_UDP_CSUM]; - /* - * Not fetching from L2TP_ATTR_UDP_ZERO_CSUM6_{T,R}X because the - * kernel doesn't send it so just leave it as default value. - */ - p->udp6_csum_tx = 1; - p->udp6_csum_rx = 1; + if (attrs[L2TP_ATTR_UDP_CSUM]) + p->udp_csum = !!rta_getattr_u8(attrs[L2TP_ATTR_UDP_CSUM]); + + p->udp6_csum_tx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]; + p->udp6_csum_rx = !attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]; + if (attrs[L2TP_ATTR_COOKIE]) memcpy(p->cookie, RTA_DATA(attrs[L2TP_ATTR_COOKIE]), p->cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_COOKIE])); @@ -304,8 +345,10 @@ static int get_response(struct nlmsghdr *n, void *arg) memcpy(p->peer_cookie, RTA_DATA(attrs[L2TP_ATTR_PEER_COOKIE]), p->peer_cookie_len = RTA_PAYLOAD(attrs[L2TP_ATTR_PEER_COOKIE])); - p->recv_seq = !!attrs[L2TP_ATTR_RECV_SEQ]; - p->send_seq = !!attrs[L2TP_ATTR_SEND_SEQ]; + if (attrs[L2TP_ATTR_RECV_SEQ]) + p->recv_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_RECV_SEQ]); + if (attrs[L2TP_ATTR_SEND_SEQ]) + p->send_seq = !!rta_getattr_u8(attrs[L2TP_ATTR_SEND_SEQ]); if (attrs[L2TP_ATTR_RECV_TIMEOUT]) p->reorder_timeout = rta_getattr_u64(attrs[L2TP_ATTR_RECV_TIMEOUT]); @@ -369,7 +412,8 @@ static int get_response(struct nlmsghdr *n, void *arg) return 0; } -static int session_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int session_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret = get_response(n, arg); @@ -389,7 +433,8 @@ static int get_session(struct l2tp_data *p) if (p->config.tunnel_id && p->config.session_id) { addattr32(&req.n, 128, L2TP_ATTR_CONN_ID, p->config.tunnel_id); - addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, p->config.session_id); + addattr32(&req.n, 128, L2TP_ATTR_SESSION_ID, + p->config.session_id); } if (rtnl_send(&genl_rth, &req, req.n.nlmsg_len) < 0) @@ -403,7 +448,8 @@ static int get_session(struct l2tp_data *p) return 0; } -static int tunnel_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +static int tunnel_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) { int ret = get_response(n, arg); @@ -468,31 +514,33 @@ static void usage(void) __attribute__((noreturn)); static void usage(void) { - fprintf(stderr, "Usage: ip l2tp add tunnel\n"); - fprintf(stderr, " remote ADDR local ADDR\n"); - fprintf(stderr, " tunnel_id ID peer_tunnel_id ID\n"); - fprintf(stderr, " [ encap { ip | udp } ]\n"); - fprintf(stderr, " [ udp_sport PORT ] [ udp_dport PORT ]\n"); - fprintf(stderr, " [ udp_csum { on | off } ]\n"); - fprintf(stderr, " [ udp6_csum_tx { on | off } ]\n"); - fprintf(stderr, " [ udp6_csum_rx { on | off } ]\n"); - fprintf(stderr, "Usage: ip l2tp add session [ name NAME ]\n"); - fprintf(stderr, " tunnel_id ID\n"); - fprintf(stderr, " session_id ID peer_session_id ID\n"); - fprintf(stderr, " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n"); - fprintf(stderr, " [ offset OFFSET ] [ peer_offset OFFSET ]\n"); - fprintf(stderr, " [ l2spec_type L2SPEC ]\n"); - fprintf(stderr, " ip l2tp del tunnel tunnel_id ID\n"); - fprintf(stderr, " ip l2tp del session tunnel_id ID session_id ID\n"); - fprintf(stderr, " ip l2tp show tunnel [ tunnel_id ID ]\n"); - fprintf(stderr, " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Where: NAME := STRING\n"); - fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n"); - fprintf(stderr, " PORT := { 0..65535 }\n"); - fprintf(stderr, " ID := { 1..4294967295 }\n"); - fprintf(stderr, " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n"); - fprintf(stderr, " L2SPEC := { none | default }\n"); + fprintf(stderr, "Usage: ip l2tp add tunnel\n" + " remote ADDR local ADDR\n" + " tunnel_id ID peer_tunnel_id ID\n" + " [ encap { ip | udp } ]\n" + " [ udp_sport PORT ] [ udp_dport PORT ]\n" + " [ udp_csum { on | off } ]\n" + " [ udp6_csum_tx { on | off } ]\n" + " [ udp6_csum_rx { on | off } ]\n" + "Usage: ip l2tp add session [ name NAME ]\n" + " tunnel_id ID\n" + " session_id ID peer_session_id ID\n" + " [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n" + " [ offset OFFSET ] [ peer_offset OFFSET ]\n" + " [ seq { none | send | recv | both } ]\n" + " [ l2spec_type L2SPEC ]\n" + " ip l2tp del tunnel tunnel_id ID\n" + " ip l2tp del session tunnel_id ID session_id ID\n" + " ip l2tp show tunnel [ tunnel_id ID ]\n" + " ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n" + "\n" + "Where: NAME := STRING\n" + " ADDR := { IP_ADDRESS | any }\n" + " PORT := { 0..65535 }\n" + " ID := { 1..4294967295 }\n" + " HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n" + " L2SPEC := { none | default }\n"); + exit(-1); } @@ -648,7 +696,26 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p) p->l2spec_type = L2TP_L2SPECTYPE_NONE; p->l2spec_len = 0; } else { - fprintf(stderr, "Unknown layer2specific header type \"%s\"\n", *argv); + fprintf(stderr, + "Unknown layer2specific header type \"%s\"\n", + *argv); + exit(-1); + } + } else if (strcmp(*argv, "seq") == 0) { + NEXT_ARG(); + if (strcasecmp(*argv, "both") == 0) { + p->recv_seq = 1; + p->send_seq = 1; + } else if (strcasecmp(*argv, "recv") == 0) { + p->recv_seq = 1; + } else if (strcasecmp(*argv, "send") == 0) { + p->send_seq = 1; + } else if (strcasecmp(*argv, "none") == 0) { + p->recv_seq = 0; + p->send_seq = 0; + } else { + fprintf(stderr, + "Unknown seq value \"%s\"\n", *argv); exit(-1); } } else if (strcmp(*argv, "tunnel") == 0) { @@ -779,6 +846,7 @@ int do_ipl2tp(int argc, char **argv) matches(*argv, "list") == 0) return do_show(argc-1, argv+1); - fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv); + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv); exit(-1); } diff --git a/ip/iplink.c b/ip/iplink.c index a8b49c52..1e603e70 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -98,8 +98,8 @@ void iplink_usage(void) fprintf(stderr, " ip link help [ TYPE ]\n\n" "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n" - " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n" - " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n" + " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" + " gre | gretap | ip6gre | ip6gretap | vti | nlmon | team_slave |\n" " bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }\n"); } exit(-1); diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c index 83ff961b..b9a146f2 100644 --- a/ip/iplink_macvlan.c +++ b/ip/iplink_macvlan.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -29,7 +30,11 @@ static void print_explain(struct link_util *lu, FILE *f) { fprintf(f, - "Usage: ... %s mode { private | vepa | bridge | passthru [nopromisc] }\n", + "Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n" + "MODE: private | vepa | bridge | passthru | source\n" + "MODE_FLAG: null | nopromisc\n" + "MODE_OPTS: for mode \"source\":\n" + "\tmacaddr { { add | del } | set [ [ ... ] ] | flush }\n", lu->id ); } @@ -43,7 +48,15 @@ static void explain(struct link_util *lu) static int mode_arg(const char *arg) { fprintf(stderr, - "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\" or \"passthru\", not \"%s\"\n", + "Error: argument of \"mode\" must be \"private\", \"vepa\", \"bridge\", \"passthru\" or \"source\", not \"%s\"\n", + arg); + return -1; +} + +static int flag_arg(const char *arg) +{ + fprintf(stderr, + "Error: argument of \"flag\" must be \"nopromisc\" or \"null\", not \"%s\"\n", arg); return -1; } @@ -53,6 +66,10 @@ static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, { __u32 mode = 0; __u16 flags = 0; + __u32 mac_mode = 0; + int has_flags = 0; + char mac[ETH_ALEN]; + struct rtattr *nmac; while (argc > 0) { if (matches(*argv, "mode") == 0) { @@ -66,10 +83,72 @@ static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, mode = MACVLAN_MODE_BRIDGE; else if (strcmp(*argv, "passthru") == 0) mode = MACVLAN_MODE_PASSTHRU; + else if (strcmp(*argv, "source") == 0) + mode = MACVLAN_MODE_SOURCE; else return mode_arg(*argv); + } else if (matches(*argv, "flag") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "nopromisc") == 0) + flags |= MACVLAN_FLAG_NOPROMISC; + else if (strcmp(*argv, "null") == 0) + flags |= 0; + else + return flag_arg(*argv); + + has_flags = 1; + + } else if (matches(*argv, "macaddr") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "add") == 0) { + mac_mode = MACVLAN_MACADDR_ADD; + } else if (strcmp(*argv, "del") == 0) { + mac_mode = MACVLAN_MACADDR_DEL; + } else if (strcmp(*argv, "set") == 0) { + mac_mode = MACVLAN_MACADDR_SET; + } else if (strcmp(*argv, "flush") == 0) { + mac_mode = MACVLAN_MACADDR_FLUSH; + } else { + explain(lu); + return -1; + } + + addattr32(n, 1024, IFLA_MACVLAN_MACADDR_MODE, mac_mode); + + if (mac_mode == MACVLAN_MACADDR_ADD || + mac_mode == MACVLAN_MACADDR_DEL) { + NEXT_ARG(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) + return -1; + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, &mac, + ETH_ALEN); + } + + if (mac_mode == MACVLAN_MACADDR_SET) { + nmac = addattr_nest(n, 1024, + IFLA_MACVLAN_MACADDR_DATA); + while (NEXT_ARG_OK()) { + NEXT_ARG_FWD(); + + if (ll_addr_a2n(mac, sizeof(mac), + *argv) != ETH_ALEN) { + PREV_ARG(); + break; + } + + addattr_l(n, 1024, IFLA_MACVLAN_MACADDR, + &mac, ETH_ALEN); + } + addattr_nest_end(n, nmac); + } } else if (matches(*argv, "nopromisc") == 0) { flags |= MACVLAN_FLAG_NOPROMISC; + has_flags = 1; } else if (matches(*argv, "help") == 0) { explain(lu); return -1; @@ -84,7 +163,7 @@ static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv, if (mode) addattr32(n, 1024, IFLA_MACVLAN_MODE, mode); - if (flags) { + if (has_flags) { if (flags & MACVLAN_FLAG_NOPROMISC && mode != MACVLAN_MODE_PASSTHRU) { pfx_err(lu, "nopromisc flag only valid in passthru mode"); @@ -100,6 +179,10 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] { __u32 mode; __u16 flags; + __u32 count; + unsigned char *addr; + int len; + struct rtattr *rta; if (!tb) return; @@ -109,20 +192,49 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] return; mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]); - fprintf(f, " mode %s ", + fprintf(f, "mode %s ", mode == MACVLAN_MODE_PRIVATE ? "private" : mode == MACVLAN_MODE_VEPA ? "vepa" : mode == MACVLAN_MODE_BRIDGE ? "bridge" : mode == MACVLAN_MODE_PASSTHRU ? "passthru" + : mode == MACVLAN_MODE_SOURCE ? "source" : "unknown"); if (!tb[IFLA_MACVLAN_FLAGS] || RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16)) - return; + flags = 0; + else + flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); - flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); if (flags & MACVLAN_FLAG_NOPROMISC) fprintf(f, "nopromisc "); + + /* in source mode, there are more options to print */ + + if (mode != MACVLAN_MODE_SOURCE) + return; + + if (!tb[IFLA_MACVLAN_MACADDR_COUNT] || + RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_COUNT]) < sizeof(__u32)) + return; + + count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); + fprintf(f, "remotes (%d) ", count); + + if (!tb[IFLA_MACVLAN_MACADDR_DATA]) + return; + + rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]); + len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]); + + for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + if (rta->rta_type != IFLA_MACVLAN_MACADDR || + RTA_PAYLOAD(rta) < 6) + continue; + addr = RTA_DATA(rta); + fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + } } static void macvlan_print_help(struct link_util *lu, int argc, char **argv, diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c index c9252bb2..aa89a00f 100644 --- a/ip/ipmacsec.c +++ b/ip/ipmacsec.c @@ -634,10 +634,10 @@ static void print_one_stat(const char **names, struct rtattr **attr, int idx, } static const char *txsc_stats_names[NUM_MACSEC_TXSC_STATS_ATTR] = { - [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutOctetsProtected", - [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutOctetsEncrypted", - [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutPktsProtected", - [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutPktsEncrypted", + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED] = "OutPktsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED] = "OutPktsEncrypted", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED] = "OutOctetsProtected", + [MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED] = "OutOctetsEncrypted", }; static void print_txsc_stats(const char *prefix, struct rtattr *attr) diff --git a/ip/iproute.c b/ip/iproute.c index 15273bee..e433de8b 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -321,7 +321,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { fprintf(stderr, "Not a route: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); - return 0; + return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) return 0; @@ -1755,7 +1755,7 @@ static int iproute_get(int argc, char **argv) if (print_route(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); - exit(1); + return -1; } if (req.n.nlmsg_type != RTM_NEWROUTE) { diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 22799355..21a76363 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -43,7 +43,7 @@ void rtnl_close(struct rtnl_handle *rth) } } -int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, int protocol) { socklen_t addr_len; @@ -58,12 +58,14 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, return -1; } - if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, + &sndbuf, sizeof(sndbuf)) < 0) { perror("SO_SNDBUF"); return -1; } - if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)) < 0) { perror("SO_RCVBUF"); return -1; } @@ -72,12 +74,14 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, rth->local.nl_family = AF_NETLINK; rth->local.nl_groups = subscriptions; - if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + if (bind(rth->fd, (struct sockaddr *)&rth->local, + sizeof(rth->local)) < 0) { perror("Cannot bind netlink socket"); return -1; } addr_len = sizeof(rth->local); - if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + if (getsockname(rth->fd, (struct sockaddr *)&rth->local, + &addr_len) < 0) { perror("Cannot getsockname"); return -1; } @@ -86,14 +90,15 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, return -1; } if (rth->local.nl_family != AF_NETLINK) { - fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + fprintf(stderr, "Wrong address family %d\n", + rth->local.nl_family); return -1; } rth->seq = time(NULL); return 0; } -int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) { return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); } @@ -123,7 +128,7 @@ int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type, .ext_filter_mask = filt_mask, }; - return send(rth->fd, (void*)&req, sizeof(req), 0); + return send(rth->fd, &req, sizeof(req), 0); } int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int family, int type, @@ -149,7 +154,7 @@ int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int family, int type, if (err) return err; - return send(rth->fd, (void*)&req, sizeof(req), 0); + return send(rth->fd, &req, req.nlh.nlmsg_len, 0); } int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type, @@ -169,7 +174,7 @@ int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type, req.ifsm.family = fam; req.ifsm.filter_mask = filt_mask; - return send(rth->fd, (void *)&req, sizeof(req), 0); + return send(rth->fd, &req, sizeof(req), 0); } int rtnl_send(struct rtnl_handle *rth, const void *buf, int len) @@ -198,7 +203,8 @@ int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int len) for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) { if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) fprintf(stderr, "ERROR truncated\n"); else @@ -237,7 +243,7 @@ int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) { struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov = { - .iov_base = (void*) n, + .iov_base = n, .iov_len = n->nlmsg_len }; struct msghdr msg = { @@ -295,7 +301,8 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, fwrite(buf, 1, NLMSG_ALIGN(status), rth->dump_fp); for (a = arg; a->filter; a++) { - struct nlmsghdr *h = (struct nlmsghdr*)buf; + struct nlmsghdr *h = (struct nlmsghdr *)buf; + msglen = status; while (NLMSG_OK(h, msglen)) { @@ -316,7 +323,8 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, break; /* process next filter */ } if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { fprintf(stderr, "ERROR truncated\n"); @@ -377,11 +385,11 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, struct nlmsghdr *answer, size_t maxlen) { int status; - unsigned seq; + unsigned int seq; struct nlmsghdr *h; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; struct iovec iov = { - .iov_base = (void*) n, + .iov_base = n, .iov_len = n->nlmsg_len }; struct msghdr msg = { @@ -420,19 +428,23 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, return -1; } if (msg.msg_namelen != sizeof(nladdr)) { - fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + fprintf(stderr, + "sender address length == %d\n", + msg.msg_namelen); exit(1); } - for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; int l = len - sizeof(*h); - if (l < 0 || len>status) { + if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Truncated message\n"); return -1; } - fprintf(stderr, "!!!malformed message: len=%d\n", len); + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); exit(1); } @@ -441,12 +453,13 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, h->nlmsg_seq != seq) { /* Don't forget to skip that message. */ status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); continue; } if (h->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { fprintf(stderr, "ERROR truncated\n"); } else if (!err->error) { @@ -473,7 +486,7 @@ int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, fprintf(stderr, "Unexpected reply!!!\n"); status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { @@ -545,7 +558,9 @@ int rtnl_listen(struct rtnl_handle *rtnl, return -1; } if (msg.msg_namelen != sizeof(nladdr)) { - fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + fprintf(stderr, + "Sender address length == %d\n", + msg.msg_namelen); exit(1); } @@ -563,17 +578,19 @@ int rtnl_listen(struct rtnl_handle *rtnl, } } - for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + for (h = (struct nlmsghdr *)buf; status >= sizeof(*h); ) { int err; int len = h->nlmsg_len; int l = len - sizeof(*h); - if (l<0 || len>status) { + if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Truncated message\n"); return -1; } - fprintf(stderr, "!!!malformed message: len=%d\n", len); + fprintf(stderr, + "!!!malformed message: len=%d\n", + len); exit(1); } @@ -582,7 +599,7 @@ int rtnl_listen(struct rtnl_handle *rtnl, return err; status -= NLMSG_ALIGN(len); - h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Message truncated\n"); @@ -600,8 +617,8 @@ int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler, { int status; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; - char buf[16384]; - struct nlmsghdr *h = (void*)buf; + char buf[16384]; + struct nlmsghdr *h = (struct nlmsghdr *)buf; while (1) { int err, len; @@ -621,7 +638,7 @@ int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler, len = h->nlmsg_len; l = len - sizeof(*h); - if (l<0 || len>sizeof(buf)) { + if (l < 0 || len > sizeof(buf)) { fprintf(stderr, "!!!malformed message: len=%d @%lu\n", len, ftell(rtnl)); return -1; @@ -681,7 +698,9 @@ int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { - fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + fprintf(stderr, + "addattr_l ERROR: message exceeded bound of %d\n", + maxlen); return -1; } rta = NLMSG_TAIL(n); @@ -695,7 +714,9 @@ int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) { if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { - fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + fprintf(stderr, + "addraw_l ERROR: message exceeded bound of %d\n", + maxlen); return -1; } @@ -744,10 +765,12 @@ int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) struct rtattr *subrta; if (RTA_ALIGN(rta->rta_len) + len > maxlen) { - fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + fprintf(stderr, + "rta_addattr32: Error! max allowed bound %d exceeded\n", + maxlen); return -1; } - subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; memcpy(RTA_DATA(subrta), &data, 4); @@ -762,10 +785,12 @@ int rta_addattr_l(struct rtattr *rta, int maxlen, int type, int len = RTA_LENGTH(alen); if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { - fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + fprintf(stderr, + "rta_addattr_l: Error! max allowed bound %d exceeded\n", + maxlen); return -1; } - subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; memcpy(RTA_DATA(subrta), data, alen); @@ -819,14 +844,16 @@ int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, type = rta->rta_type & ~flags; if ((type <= max) && (!tb[type])) tb[type] = rta; - rta = RTA_NEXT(rta,len); + rta = RTA_NEXT(rta, len); } if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return 0; } -int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +int parse_rtattr_byindex(struct rtattr *tb[], int max, + struct rtattr *rta, int len) { int i = 0; @@ -834,10 +861,11 @@ int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int l while (RTA_OK(rta, len)) { if (rta->rta_type <= max && i < max) tb[i++] = rta; - rta = RTA_NEXT(rta,len); + rta = RTA_NEXT(rta, len); } if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return i; } @@ -848,13 +876,16 @@ struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len) return rta; rta = RTA_NEXT(rta, len); } + if (len) - fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", + len, rta->rta_len); return NULL; } -int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, - int len) +int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, + struct rtattr *rta, + int len) { if (RTA_PAYLOAD(rta) < len) return -1; diff --git a/lib/ll_types.c b/lib/ll_types.c index 2c5bf8b5..eca617f3 100644 --- a/lib/ll_types.c +++ b/lib/ll_types.c @@ -100,11 +100,13 @@ __PF(IEEE80211,ieee802.11) __PF(IEEE80211_PRISM,ieee802.11/prism) __PF(IEEE80211_RADIOTAP,ieee802.11/radiotap) __PF(IEEE802154, ieee802.15.4) +__PF(IEEE802154_MONITOR, ieee802.15.4/monitor) __PF(PHONET, phonet) __PF(PHONET_PIPE, phonet_pipe) __PF(CAIF, caif) __PF(IP6GRE, gre6) __PF(NETLINK, netlink) +__PF(6LOWPAN, 6lowpan) __PF(NONE, none) __PF(VOID,void) diff --git a/lib/rt_names.c b/lib/rt_names.c index b665d3e9..c66cb1e4 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -559,8 +559,12 @@ const char *rtnl_group_n2a(int id, char *buf, int len) for (i = 0; i < 256; i++) { entry = rtnl_group_hash[i]; - if (entry && entry->id == id) - return entry->name; + + while (entry) { + if (entry->id == id) + return entry->name; + entry = entry->next; + } } snprintf(buf, len, "%d", id); diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index 9ce31937..6bfe66f8 100644 --- a/man/man8/devlink-dev.8 +++ b/man/man8/devlink-dev.8 @@ -31,6 +31,9 @@ devlink-dev \- devlink device configuration .RI "[ " .BR mode " { " legacy " | " switchdev " } " .RI "]" +.RI "[ " +.BR inline-mode " { " none " | " link " | " network " | " transport " } " +.RI "]" .ti -8 .BR "devlink dev eswitch show" @@ -54,7 +57,7 @@ BUS_NAME/BUS_ADDRESS .TP .BR mode " { " legacy " | " switchdev " } " -set eswitch mode +Set eswitch mode .I legacy - Legacy SRIOV @@ -62,6 +65,22 @@ set eswitch mode .I switchdev - SRIOV switchdev offloads +.TP +.BR inline-mode " { " none " | " link " | " network " | " transport " } " +Some HWs need the VF driver to put part of the packet headers on the TX descriptor so the e-switch can do proper matching and steering. + +.I none +- None + +.I link +- L2 mode + +.I network +- L3 mode + +.I transport +- L4 mode + .SH "EXAMPLES" .PP devlink dev show diff --git a/man/man8/ip-l2tp.8 b/man/man8/ip-l2tp.8 index 5b7041f9..8ce630a6 100644 --- a/man/man8/ip-l2tp.8 +++ b/man/man8/ip-l2tp.8 @@ -30,6 +30,12 @@ ip-l2tp - L2TPv3 static unmanaged tunnel configuration .IR PORT .RB " ]" .br +.RB "[ " udp_csum " { " on " | " off " } ]" +.br +.RB "[ " udp6_csum_tx " { " on " | " off " } ]" +.br +.RB "[ " udp6_csum_rx " { " on " | " off " } ]" +.br .ti -8 .BR "ip l2tp add session" .RB "[ " name @@ -51,6 +57,8 @@ ip-l2tp - L2TPv3 static unmanaged tunnel configuration .br .RB "[ " l2spec_type " { " none " | " default " } ]" .br +.RB "[ " seq " { " none " | " send " | " recv " | " both " } ]" +.br .RB "[ " offset .IR OFFSET .RB " ] [ " peer_offset @@ -154,9 +162,6 @@ tunnels and sessions to be established and provides for detecting and acting upon network failures. .SS ip l2tp add tunnel - add a new tunnel .TP -.BI name " NAME " -sets the session network interface name. Default is l2tpethN. -.TP .BI tunnel_id " ID" set the tunnel id, which is a 32-bit integer value. Uniquely identifies the tunnel. The value used must match the peer_tunnel_id @@ -191,6 +196,33 @@ selected. set the UDP destination port to be used for the tunnel. Must be present when udp encapsulation is selected. Ignored when ip encapsulation is selected. +.TP +.BI udp_csum " STATE" +(IPv4 only) control if IPv4 UDP checksums should be calculated and checked for the +encapsulating UDP packets, when UDP encapsulating is selected. +Default is +.BR off "." +.br +Valid values are: +.BR on ", " off "." +.TP +.BI udp6_csum_tx " STATE" +(IPv6 only) control if IPv6 UDP checksums should be calculated for encapsulating +UDP packets, when UDP encapsulating is selected. +Default is +.BR on "." +.br +Valid values are: +.BR on ", " off "." +.TP +.BI udp6_csum_rx " STATE" +(IPv6 only) control if IPv6 UDP checksums should be checked for the encapsulating +UDP packets, when UDP encapsulating is selected. +Default is +.BR on "." +.br +Valid values are: +.BR on ", " off "." .SS ip l2tp del tunnel - destroy a tunnel .TP .BI tunnel_id " ID" @@ -239,7 +271,20 @@ find in received L2TP packets. Default is to use no cookie. set the layer2specific header type of the session. .br Valid values are: -.BR none ", " udp "." +.BR none ", " default "." +.TP +.BI seq " SEQ" +controls sequence numbering to prevent or detect out of order packets. +.B send +puts a sequence number in the default layer2specific header of each +outgoing packet. +.B recv +reorder packets if they are received out of order. +Default is +.BR none "." +.br +Valid values are: +.BR none ", " send ", " recv ", " both "." .TP .BI offset " OFFSET" sets the byte offset from the L2TP header where user data starts in diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index ee1159da..18e94171 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -135,7 +135,12 @@ ip-link \- network device configuration .IR NAME " ]" .br .RB "[ " addrgenmode " { " eui64 " | " none " | " stable_secret " | " random " } ]" - +.br +.RB "[ " macaddr " { " flush " | { " add " | " del " } " +.IR MACADDR " | set [ " +.IR MACADDR " [ " +.IR MACADDR " [ ... ] ] ] } ]" +.br .ti -8 .B ip link show @@ -882,7 +887,7 @@ the following additional arguments are supported: .BI "ip link add link " DEVICE " name " NAME .BR type " { " macvlan " | " macvtap " } " .BR mode " { " private " | " vepa " | " bridge " | " passthru -.RB " [ " nopromisc " ] } " +.RB " [ " nopromisc " ] | " source " } " .in +8 .sp @@ -919,6 +924,13 @@ the interface or create vlan interfaces on top of it. By default, this mode forces the underlying interface into promiscuous mode. Passing the .BR nopromisc " flag prevents this, so the promisc flag may be controlled " using standard tools. + +.B mode source +- allows one to set a list of allowed mac address, which is used to match +against source mac address from received frames on underlying interface. This +allows creating mac based VLAN associations, instead of standard port or tag +based. The feature is useful to deploy 802.1x mac based behavior, +where drivers of underlying interfaces doesn't allows that. .in -8 .TP @@ -1468,6 +1480,32 @@ the following additional arguments are supported: .in -8 +.TP +MACVLAN and MACVTAP Support +Modify list of allowed macaddr for link in source mode. + +.B "ip link set type { macvlan | macvap } " +[ +.BI macaddr " " "" COMMAND " " MACADDR " ..." +] + +Commands: +.in +8 +.B add +- add MACADDR to allowed list +.sp +.B set +- replace allowed list +.sp +.B del +- remove MACADDR from allowed list +.sp +.B flush +- flush whole allowed list +.sp +.in -8 + + .SS ip link show - display device attributes .TP diff --git a/man/man8/ss.8 b/man/man8/ss.8 index 8911976f..4ef11523 100644 --- a/man/man8/ss.8 +++ b/man/man8/ss.8 @@ -122,6 +122,9 @@ Display RAW sockets. .B \-x, \-\-unix Display Unix domain sockets (alias for -f unix). .TP +.B \-S, \-\-sctp +Display SCTP sockets. +.TP .B \-f FAMILY, \-\-family=FAMILY Display sockets of type FAMILY. Currently the following families are supported: unix, inet, inet6, link, netlink. diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index 74f76647..16ef2617 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -23,8 +23,6 @@ flower \- flow based traffic control filter .R " | { " .BR dst_mac " | " src_mac " } " .IR mac_address " | " -.BR eth_type " { " ipv4 " | " ipv6 " | " 802.1Q " | " -.IR ETH_TYPE " } | " .B vlan_id .IR VID " | " .B vlan_prio @@ -75,13 +73,6 @@ Do not process filter by hardware. .BI src_mac " mac_address" Match on source or destination MAC address. .TP -.BI eth_type " ETH_TYPE" -Match on the next protocol. -.I ETH_TYPE -may be either -.BR ipv4 , ipv6 , 802.1Q , -or an unsigned 16bit value in hexadecimal format. -.TP .BI vlan_id " VID" Match on vlan tag id. .I VID diff --git a/man/man8/tc.8 b/man/man8/tc.8 index 7ee1c9c9..8a47a2b9 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -28,7 +28,7 @@ class-id ] qdisc .B tc .RI "[ " OPTIONS " ]" -.B filter [ add | change | replace | delete ] dev +.B filter [ add | change | replace | delete | get ] dev DEV .B [ parent qdisc-id @@ -575,6 +575,14 @@ replace Performs a nearly atomic remove/add on an existing node id. If the node does not exist yet it is created. +.TP +get +Displays a single filter given the interface, parent ID, priority, protocol and handle ID. + +.TP +show +Displays all filters attached to the given interface. A valid parent ID must be passed. + .TP link Only available for qdiscs and performs a replace where the node diff --git a/misc/ifstat.c b/misc/ifstat.c index d5519737..92d67b0c 100644 --- a/misc/ifstat.c +++ b/misc/ifstat.c @@ -662,18 +662,18 @@ static void usage(void) { fprintf(stderr, "Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n" -" -h, --help this message\n" -" -a, --ignore ignore history\n" -" -d, --scan=SECS sample every statistics every SECS\n" -" -e, --errors show errors\n" -" -j, --json format output in JSON\n" -" -n, --nooutput do history only\n" -" -p, --pretty pretty print\n" -" -r, --reset reset history\n" -" -s, --noupdate don\'t update history\n" -" -t, --interval=SECS report average over the last SECS\n" -" -V, --version output version information\n" -" -z, --zeros show entries with zero activity\n"); +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -e, --errors show errors\n" +" -j, --json format output in JSON\n" +" -n, --nooutput do history only\n" +" -p, --pretty pretty print\n" +" -r, --reset reset history\n" +" -s, --noupdate don't update history\n" +" -t, --interval=SECS report average over the last SECS\n" +" -V, --version output version information\n" +" -z, --zeros show entries with zero activity\n"); exit(-1); } diff --git a/misc/nstat.c b/misc/nstat.c index 1cb6c7ee..1212b1f2 100644 --- a/misc/nstat.c +++ b/misc/nstat.c @@ -526,17 +526,17 @@ static void usage(void) { fprintf(stderr, "Usage: nstat [OPTION] [ PATTERN [ PATTERN ] ]\n" -" -h, --help this message\n" -" -a, --ignore ignore history\n" -" -d, --scan=SECS sample every statistics every SECS\n" -" -j, --json format output in JSON\n" -" -n, --nooutput do history only\n" -" -p, --pretty pretty print\n" -" -r, --reset reset history\n" -" -s, --noupdate don\'t update history\n" -" -t, --interval=SECS report average over the last SECS\n" -" -V, --version output version information\n" -" -z, --zeros show entries with zero activity\n"); +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -j, --json format output in JSON\n" +" -n, --nooutput do history only\n" +" -p, --pretty pretty print\n" +" -r, --reset reset history\n" +" -s, --noupdate don't update history\n" +" -t, --interval=SECS report average over the last SECS\n" +" -V, --version output version information\n" +" -z, --zeros show entries with zero activity\n"); exit(-1); } diff --git a/misc/ss.c b/misc/ss.c index dd77b815..07dcd8c2 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -43,6 +43,7 @@ #include #include #include +#include #define MAGIC_SEQ 123456 @@ -102,6 +103,7 @@ int show_header = 1; /* If show_users & show_proc_ctx only do user_ent_hash_build() once */ int user_ent_hash_build_init; int follow_events; +int sctp_ino; int netid_width; int state_width; @@ -111,6 +113,7 @@ int serv_width; int screen_width; static const char *TCP_PROTO = "tcp"; +static const char *SCTP_PROTO = "sctp"; static const char *UDP_PROTO = "udp"; static const char *RAW_PROTO = "raw"; static const char *dg_proto; @@ -126,13 +129,15 @@ enum { PACKET_DG_DB, PACKET_R_DB, NETLINK_DB, + SCTP_DB, MAX_DB }; #define PACKET_DBM ((1<ino) + return false; + return true; +} + static void sock_state_print(struct sockstat *s, const char *sock_name) { if (netid_width) - printf("%-*s ", netid_width, sock_name); - if (state_width) - printf("%-*s ", state_width, sstate_name[s->state]); + printf("%-*s ", netid_width, + is_sctp_assoc(s, sock_name) ? "" : sock_name); + if (state_width) { + if (is_sctp_assoc(s, sock_name)) + printf("`- %-*s ", state_width - 3, + sctp_sstate_name[s->state]); + else + printf("%-*s ", state_width, sstate_name[s->state]); + } printf("%-6d %-6d ", s->rq, s->wq); } @@ -908,6 +958,8 @@ static void init_service_resolver(void) c->proto = TCP_PROTO; else if (strcmp(proto, UDP_PROTO) == 0) c->proto = UDP_PROTO; + else if (strcmp(proto, SCTP_PROTO) == 0) + c->proto = SCTP_PROTO; else c->proto = NULL; c->next = rlist; @@ -1679,6 +1731,8 @@ static char *proto_name(int protocol) return "udp"; case IPPROTO_TCP: return "tcp"; + case IPPROTO_SCTP: + return "sctp"; case IPPROTO_DCCP: return "dccp"; } @@ -1771,6 +1825,56 @@ static char *sprint_bw(char *buf, double bw) return buf; } +static void sctp_stats_print(struct sctp_info *s) +{ + if (s->sctpi_tag) + printf(" tag:%x", s->sctpi_tag); + if (s->sctpi_state) + printf(" state:%s", sctp_sstate_name[s->sctpi_state]); + if (s->sctpi_rwnd) + printf(" rwnd:%d", s->sctpi_rwnd); + if (s->sctpi_unackdata) + printf(" unackdata:%d", s->sctpi_unackdata); + if (s->sctpi_penddata) + printf(" penddata:%d", s->sctpi_penddata); + if (s->sctpi_instrms) + printf(" instrms:%d", s->sctpi_instrms); + if (s->sctpi_outstrms) + printf(" outstrms:%d", s->sctpi_outstrms); + if (s->sctpi_inqueue) + printf(" inqueue:%d", s->sctpi_inqueue); + if (s->sctpi_outqueue) + printf(" outqueue:%d", s->sctpi_outqueue); + if (s->sctpi_overall_error) + printf(" overerr:%d", s->sctpi_overall_error); + if (s->sctpi_max_burst) + printf(" maxburst:%d", s->sctpi_max_burst); + if (s->sctpi_maxseg) + printf(" maxseg:%d", s->sctpi_maxseg); + if (s->sctpi_peer_rwnd) + printf(" prwnd:%d", s->sctpi_peer_rwnd); + if (s->sctpi_peer_tag) + printf(" ptag:%x", s->sctpi_peer_tag); + if (s->sctpi_peer_capable) + printf(" pcapable:%d", s->sctpi_peer_capable); + if (s->sctpi_peer_sack) + printf(" psack:%d", s->sctpi_peer_sack); + if (s->sctpi_s_autoclose) + printf(" autoclose:%d", s->sctpi_s_autoclose); + if (s->sctpi_s_adaptation_ind) + printf(" adapind:%d", s->sctpi_s_adaptation_ind); + if (s->sctpi_s_pd_point) + printf(" pdpoint:%d", s->sctpi_s_pd_point); + if (s->sctpi_s_nodelay) + printf(" nodealy:%d", s->sctpi_s_nodelay); + if (s->sctpi_s_disable_fragments) + printf(" nofrag:%d", s->sctpi_s_disable_fragments); + if (s->sctpi_s_v4mapped) + printf(" v4mapped:%d", s->sctpi_s_v4mapped); + if (s->sctpi_s_frag_interleave) + printf(" fraginl:%d", s->sctpi_s_frag_interleave); +} + static void tcp_stats_print(struct tcpstat *s) { char b1[64]; @@ -1902,6 +2006,13 @@ static void tcp_timer_print(struct tcpstat *s) } } +static void sctp_timer_print(struct tcpstat *s) +{ + if (s->timer) + printf(" timer:(T3_RTX,%s,%d)", + print_ms_timer(s->timeout), s->retrans); +} + static int tcp_show_line(char *line, const struct filter *f, int family) { int rto = 0, ato = 0; @@ -2168,6 +2279,64 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, } } +static const char *format_host_sa(struct sockaddr_storage *sa) +{ + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } *saddr = (void *)sa; + + switch (sa->ss_family) { + case AF_INET: + return format_host(AF_INET, 4, &saddr->sin.sin_addr); + case AF_INET6: + return format_host(AF_INET6, 16, &saddr->sin6.sin6_addr); + default: + return ""; + } +} + +static void sctp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, + struct rtattr *tb[]) +{ + struct sockaddr_storage *sa; + int len; + + print_skmeminfo(tb, INET_DIAG_SKMEMINFO); + + if (tb[INET_DIAG_LOCALS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]); + sa = RTA_DATA(tb[INET_DIAG_LOCALS]); + + printf("locals:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + + } + if (tb[INET_DIAG_PEERS]) { + len = RTA_PAYLOAD(tb[INET_DIAG_PEERS]); + sa = RTA_DATA(tb[INET_DIAG_PEERS]); + + printf(" peers:%s", format_host_sa(sa)); + for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa)) + printf(",%s", format_host_sa(sa)); + } + if (tb[INET_DIAG_INFO]) { + struct sctp_info *info; + len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); + + /* workaround for older kernels with less fields */ + if (len < sizeof(*info)) { + info = alloca(sizeof(*info)); + memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); + memset((char *)info + len, 0, sizeof(*info) - len); + } else + info = RTA_DATA(tb[INET_DIAG_INFO]); + + sctp_stats_print(info); + } +} + static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s) { struct rtattr *tb[INET_DIAG_MAX+1]; @@ -2221,7 +2390,10 @@ static int inet_show_sock(struct nlmsghdr *nlh, t.timer = r->idiag_timer; t.timeout = r->idiag_expires; t.retrans = r->idiag_retrans; - tcp_timer_print(&t); + if (protocol == IPPROTO_SCTP) + sctp_timer_print(&t); + else + tcp_timer_print(&t); } if (show_details) { @@ -2242,8 +2414,12 @@ static int inet_show_sock(struct nlmsghdr *nlh, if (show_mem || show_tcpinfo) { printf("\n\t"); - tcp_show_info(nlh, r, tb); + if (protocol == IPPROTO_SCTP) + sctp_show_info(nlh, r, tb); + else + tcp_show_info(nlh, r, tb); } + sctp_ino = s->ino; printf("\n"); return 0; @@ -2627,6 +2803,17 @@ outerr: } while (0); } +static int sctp_show(struct filter *f) +{ + if (!filter_af_get(f, AF_INET) && !filter_af_get(f, AF_INET6)) + return 0; + + if (!getenv("PROC_NET_SCTP") && !getenv("PROC_ROOT") + && inet_show_netlink(f, NULL, IPPROTO_SCTP) == 0) + return 0; + + return 0; +} static int dgram_show_line(char *line, const struct filter *f, int family) { @@ -2895,7 +3082,9 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh, memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len); name[len] = '\0'; if (name[0] == '\0') - name[0] = '@'; + for (int i = 0; i < len; i++) + if (name[i] == '\0') + name[i] = '@'; stat.name = &name[0]; memcpy(stat.local.data, &stat.name, sizeof(stat.name)); } @@ -3738,6 +3927,7 @@ static void _usage(FILE *dest) " -6, --ipv6 display only IP version 6 sockets\n" " -0, --packet display PACKET sockets\n" " -t, --tcp display only TCP sockets\n" +" -S, --sctp display only SCTP sockets\n" " -u, --udp display only UDP sockets\n" " -d, --dccp display only DCCP sockets\n" " -w, --raw display only RAW sockets\n" @@ -3820,6 +4010,7 @@ static const struct option long_opts[] = { { "events", 0, 0, 'E' }, { "dccp", 0, 0, 'd' }, { "tcp", 0, 0, 't' }, + { "sctp", 0, 0, 'S' }, { "udp", 0, 0, 'u' }, { "raw", 0, 0, 'w' }, { "unix", 0, 0, 'x' }, @@ -3855,7 +4046,8 @@ int main(int argc, char *argv[]) int ch; int state_filter = 0; - while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KH", + while ((ch = getopt_long(argc, argv, + "dhaletuwxnro460spbEf:miA:D:F:vVzZN:KHS", long_opts, NULL)) != EOF) { switch (ch) { case 'n': @@ -3894,6 +4086,9 @@ int main(int argc, char *argv[]) case 't': filter_db_set(¤t_filter, TCP_DB); break; + case 'S': + filter_db_set(¤t_filter, SCTP_DB); + break; case 'u': filter_db_set(¤t_filter, UDP_DB); break; @@ -3958,6 +4153,7 @@ int main(int argc, char *argv[]) filter_db_set(¤t_filter, UDP_DB); filter_db_set(¤t_filter, DCCP_DB); filter_db_set(¤t_filter, TCP_DB); + filter_db_set(¤t_filter, SCTP_DB); filter_db_set(¤t_filter, RAW_DB); } else if (strcmp(p, "udp") == 0) { filter_db_set(¤t_filter, UDP_DB); @@ -3965,6 +4161,8 @@ int main(int argc, char *argv[]) filter_db_set(¤t_filter, DCCP_DB); } else if (strcmp(p, "tcp") == 0) { filter_db_set(¤t_filter, TCP_DB); + } else if (strcmp(p, "sctp") == 0) { + filter_db_set(¤t_filter, SCTP_DB); } else if (strcmp(p, "raw") == 0) { filter_db_set(¤t_filter, RAW_DB); } else if (strcmp(p, "unix") == 0) { @@ -4089,10 +4287,9 @@ int main(int argc, char *argv[]) filter_merge_defaults(¤t_filter); if (resolve_services && resolve_hosts && - (current_filter.dbs&(UNIX_DBM|(1<nlmsg_type == RTM_DELTFILTER) fprintf(fp, "deleted "); + if (n->nlmsg_type == RTM_NEWTFILTER && + (n->nlmsg_flags & NLM_F_CREATE) && + !(n->nlmsg_flags & NLM_F_EXCL)) + fprintf(fp, "replaced "); + + if (n->nlmsg_type == RTM_NEWTFILTER && + (n->nlmsg_flags & NLM_F_CREATE) && + (n->nlmsg_flags & NLM_F_EXCL)) + fprintf(fp, "added "); + fprintf(fp, "filter "); if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index 93c9a4c1..3a3701c2 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -182,7 +182,8 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) ll_init_map(&rth); - if ((idx = ll_name_to_index(d)) == 0) { + idx = ll_name_to_index(d); + if (idx == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; } @@ -198,8 +199,7 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) static int filter_ifindex; int print_qdisc(const struct sockaddr_nl *who, - struct nlmsghdr *n, - void *arg) + struct nlmsghdr *n, void *arg) { FILE *fp = (FILE *)arg; struct tcmsg *t = NLMSG_DATA(n); @@ -231,21 +231,29 @@ int print_qdisc(const struct sockaddr_nl *who, if (n->nlmsg_type == RTM_DELQDISC) fprintf(fp, "deleted "); - fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]), t->tcm_handle>>16); + if (show_raw) + fprintf(fp, "qdisc %s %x:[%08x] ", + rta_getattr_str(tb[TCA_KIND]), + t->tcm_handle >> 16, t->tcm_handle); + else + fprintf(fp, "qdisc %s %x: ", rta_getattr_str(tb[TCA_KIND]), + t->tcm_handle >> 16); + if (filter_ifindex == 0) fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + if (t->tcm_parent == TC_H_ROOT) fprintf(fp, "root "); else if (t->tcm_parent) { print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); fprintf(fp, "parent %s ", abuf); } - if (t->tcm_info != 1) { - fprintf(fp, "refcnt %d ", t->tcm_info); - } - /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ - if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND]))) + if (t->tcm_info != 1) + fprintf(fp, "refcnt %d ", t->tcm_info); + + /* 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"); else q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); @@ -257,10 +265,12 @@ int print_qdisc(const struct sockaddr_nl *who, fprintf(fp, "[cannot parse qdisc parameters]"); } fprintf(fp, "\n"); + if (show_details && tb[TCA_STAB]) { print_size_table(fp, " ", tb[TCA_STAB]); fprintf(fp, "\n"); } + if (show_stats) { struct rtattr *xstats = NULL; @@ -289,11 +299,11 @@ static int tc_qdisc_list(int argc, char **argv) strncpy(d, *argv, sizeof(d)-1); } else if (strcmp(*argv, "ingress") == 0 || strcmp(*argv, "clsact") == 0) { - if (t.tcm_parent) { - fprintf(stderr, "Duplicate parent ID\n"); - usage(); - } - t.tcm_parent = TC_H_INGRESS; + if (t.tcm_parent) { + fprintf(stderr, "Duplicate parent ID\n"); + usage(); + } + t.tcm_parent = TC_H_INGRESS; } else if (matches(*argv, "help") == 0) { usage(); } else { @@ -307,7 +317,8 @@ static int tc_qdisc_list(int argc, char **argv) ll_init_map(&rth); if (d[0]) { - if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + t.tcm_ifindex = ll_name_to_index(d); + if (t.tcm_ifindex == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return 1; }