Commit f1f1aeb2 authored by Stephen Hemminger's avatar Stephen Hemminger

Merge branch 'master' into net-next-3.11

Conflicts:
	tc/q_fq.c
parents 6d64ec02 9bea14ff
......@@ -132,12 +132,15 @@ int do_monitor(int argc, char **argv)
if (file) {
FILE *fp;
int err;
fp = fopen(file, "r");
if (fp == NULL) {
perror("Cannot fopen");
exit(-1);
}
return rtnl_from_file(fp, accept_msg, stdout);
err = rtnl_from_file(fp, accept_msg, stdout);
fclose(fp);
return err;
}
if (rtnl_open(&rth, groups) < 0)
......
#ifndef __LINUX_TC_DEF_H
#define __LINUX_TC_DEF_H
#include <linux/pkt_cls.h>
struct tc_defact {
tc_gen;
};
enum {
TCA_DEF_UNSPEC,
TCA_DEF_TM,
TCA_DEF_PARMS,
TCA_DEF_DATA,
__TCA_DEF_MAX
};
#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
#endif
......@@ -151,6 +151,7 @@ int print_timestamp(FILE *fp);
extern int cmdlineno;
extern ssize_t getcmdline(char **line, size_t *len, FILE *in);
extern int makeargs(char *line, char *argv[], int maxargs);
extern int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6);
struct iplink_req;
int iplink_parse(int argc, char **argv, struct iplink_req *req,
......
......@@ -5,7 +5,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_vti.o \
iplink_vxlan.o tcp_metrics.o iplink_ipoib.o ipnetconf.o link_ip6tnl.o \
link_iptnl.o
link_iptnl.o link_gre6.o
RTMONOBJ=rtmon.o
......@@ -23,7 +23,6 @@ all: $(TARGETS) $(SCRIPTS)
ip: $(IPOBJ) $(LIBNETLINK)
rtmon: $(RTMONOBJ)
install: all
......
......@@ -48,11 +48,12 @@ static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n");
fprintf(stderr, " [ mode { ip6ip6 | ipip6 | any } ]\n");
fprintf(stderr, " [ mode { ip6ip6 | ipip6 | ip6gre | any } ]\n");
fprintf(stderr, " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n");
fprintf(stderr, " [ encaplimit ELIM ]\n");
fprintf(stderr ," [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
fprintf(stderr, " [ dscp inherit ]\n");
fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Where: NAME := STRING\n");
fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
......@@ -62,10 +63,11 @@ static void usage(void)
DEFAULT_TNL_HOP_LIMIT);
fprintf(stderr, " TCLASS := { 0x0..0xff | inherit }\n");
fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff | inherit }\n");
fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n");
exit(-1);
}
static void print_tunnel(struct ip6_tnl_parm *p)
static void print_tunnel(struct ip6_tnl_parm2 *p)
{
char remote[64];
char local[64];
......@@ -104,9 +106,29 @@ static void print_tunnel(struct ip6_tnl_parm *p)
if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
printf(" dscp inherit");
if (p->proto == IPPROTO_GRE) {
if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
printf(" key %u", ntohl(p->i_key));
else if ((p->i_flags|p->o_flags)&GRE_KEY) {
if (p->i_flags&GRE_KEY)
printf(" ikey %u ", ntohl(p->i_key));
if (p->o_flags&GRE_KEY)
printf(" okey %u ", ntohl(p->o_key));
}
if (p->i_flags&GRE_SEQ)
printf("%s Drop packets out of sequence.\n", _SL_);
if (p->i_flags&GRE_CSUM)
printf("%s Checksum in received packet is required.", _SL_);
if (p->o_flags&GRE_SEQ)
printf("%s Sequence packets on output.", _SL_);
if (p->o_flags&GRE_CSUM)
printf("%s Checksum output packets.", _SL_);
}
}
static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
{
int count = 0;
char medium[IFNAMSIZ];
......@@ -124,6 +146,9 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
strcmp(*argv, "ipip6") == 0 ||
strcmp(*argv, "ip4ip6") == 0)
p->proto = IPPROTO_IPIP;
else if (strcmp(*argv, "ip6gre") == 0 ||
strcmp(*argv, "gre/ipv6") == 0)
p->proto = IPPROTO_GRE;
else if (strcmp(*argv, "any/ipv6") == 0 ||
strcmp(*argv, "any") == 0)
p->proto = 0;
......@@ -202,6 +227,60 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
if (strcmp(*argv, "inherit") != 0)
invarg("not inherit", *argv);
p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
} else if (strcmp(*argv, "key") == 0) {
unsigned uval;
NEXT_ARG();
p->i_flags |= GRE_KEY;
p->o_flags |= GRE_KEY;
if (strchr(*argv, '.'))
p->i_key = p->o_key = get_addr32(*argv);
else {
if (get_unsigned(&uval, *argv, 0)<0) {
fprintf(stderr, "invalid value of \"key\"\n");
exit(-1);
}
p->i_key = p->o_key = htonl(uval);
}
} else if (strcmp(*argv, "ikey") == 0) {
unsigned uval;
NEXT_ARG();
p->i_flags |= GRE_KEY;
if (strchr(*argv, '.'))
p->i_key = get_addr32(*argv);
else {
if (get_unsigned(&uval, *argv, 0)<0) {
fprintf(stderr, "invalid value of \"ikey\"\n");
exit(-1);
}
p->i_key = htonl(uval);
}
} else if (strcmp(*argv, "okey") == 0) {
unsigned uval;
NEXT_ARG();
p->o_flags |= GRE_KEY;
if (strchr(*argv, '.'))
p->o_key = get_addr32(*argv);
else {
if (get_unsigned(&uval, *argv, 0)<0) {
fprintf(stderr, "invalid value of \"okey\"\n");
exit(-1);
}
p->o_key = htonl(uval);
}
} else if (strcmp(*argv, "seq") == 0) {
p->i_flags |= GRE_SEQ;
p->o_flags |= GRE_SEQ;
} else if (strcmp(*argv, "iseq") == 0) {
p->i_flags |= GRE_SEQ;
} else if (strcmp(*argv, "oseq") == 0) {
p->o_flags |= GRE_SEQ;
} else if (strcmp(*argv, "csum") == 0) {
p->i_flags |= GRE_CSUM;
p->o_flags |= GRE_CSUM;
} else if (strcmp(*argv, "icsum") == 0) {
p->i_flags |= GRE_CSUM;
} else if (strcmp(*argv, "ocsum") == 0) {
p->o_flags |= GRE_CSUM;
} else {
if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
......@@ -212,7 +291,7 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
duparg2("name", *argv);
strncpy(p->name, *argv, IFNAMSIZ - 1);
if (cmd == SIOCCHGTUNNEL && count == 0) {
struct ip6_tnl_parm old_p;
struct ip6_tnl_parm2 old_p;
memset(&old_p, 0, sizeof(old_p));
if (tnl_get_ioctl(*argv, &old_p))
return -1;
......@@ -230,7 +309,7 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
return 0;
}
static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
static void ip6_tnl_parm_init(struct ip6_tnl_parm2 *p, int apply_default)
{
memset(p, 0, sizeof(*p));
p->proto = IPPROTO_IPV6;
......@@ -244,8 +323,8 @@ static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
* @p1: user specified parameter
* @p2: database entry
*/
static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
const struct ip6_tnl_parm *p2)
static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1,
const struct ip6_tnl_parm2 *p2)
{
return ((!p1->link || p1->link == p2->link) &&
(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
......@@ -263,7 +342,7 @@ static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
(!p1->flags || (p1->flags & p2->flags)));
}
static int do_tunnels_list(struct ip6_tnl_parm *p)
static int do_tunnels_list(struct ip6_tnl_parm2 *p)
{
char buf[512];
int err = -1;
......@@ -287,7 +366,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
rx_fifo, rx_frame,
tx_bytes, tx_packets, tx_errs, tx_drops,
tx_fifo, tx_colls, tx_carrier, rx_multi;
struct ip6_tnl_parm p1;
struct ip6_tnl_parm2 p1;
char *ptr;
buf[sizeof(buf) - 1] = '\0';
......@@ -312,10 +391,12 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
fprintf(stderr, "Failed to get type of \"%s\"\n", name);
continue;
}
if (type != ARPHRD_TUNNEL6)
if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE)
continue;
memset(&p1, 0, sizeof(p1));
ip6_tnl_parm_init(&p1, 0);
if (type == ARPHRD_IP6GRE)
p1.proto = IPPROTO_GRE;
strcpy(p1.name, name);
p1.link = ll_name_to_index(p1.name);
if (p1.link == 0)
......@@ -346,7 +427,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
static int do_show(int argc, char **argv)
{
struct ip6_tnl_parm p;
struct ip6_tnl_parm2 p;
ll_init_map(&rth);
ip6_tnl_parm_init(&p, 0);
......@@ -369,28 +450,44 @@ static int do_show(int argc, char **argv)
static int do_add(int cmd, int argc, char **argv)
{
struct ip6_tnl_parm p;
struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, cmd, &p) < 0)
return -1;
return tnl_add_ioctl(cmd,
cmd == SIOCCHGTUNNEL && p.name[0] ?
p.name : "ip6tnl0", p.name, &p);
switch (p.proto) {
case IPPROTO_IPIP:
case IPPROTO_IPV6:
return tnl_add_ioctl(cmd, "ip6tnl0", p.name, &p);
case IPPROTO_GRE:
return tnl_add_ioctl(cmd, "ip6gre0", p.name, &p);
default:
fprintf(stderr, "cannot determine tunnel mode (ip6ip6, ipip6 or gre)\n");
}
return -1;
}
static int do_del(int argc, char **argv)
{
struct ip6_tnl_parm p;
struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
return -1;
return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p);
switch (p.proto) {
case IPPROTO_IPIP:
case IPPROTO_IPV6:
return tnl_del_ioctl("ip6tnl0", p.name, &p);
case IPPROTO_GRE:
return tnl_del_ioctl("ip6gre0", p.name, &p);
default:
return tnl_del_ioctl(p.name, p.name, &p);
}
return -1;
}
int do_ip6tunnel(int argc, char **argv)
......
......@@ -84,8 +84,9 @@ void iplink_usage(void)
if (iplink_have_newlink()) {
fprintf(stderr, "\n");
fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can |\n");
fprintf(stderr, " bridge | ipoib | ip6tnl | ipip | sit | vxlan }\n");
fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
fprintf(stderr, " can | bridge | ipoib | ip6tnl | ipip | sit | vxlan |\n");
fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti }\n");
}
exit(-1);
}
......@@ -243,7 +244,7 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
}
ivt.vf = vf;
addattr_l(&req->n, sizeof(*req), IFLA_VF_TX_RATE, &ivt, sizeof(ivt));
} else if (matches(*argv, "spoofchk") == 0) {
struct ifla_vf_spoofchk ivs;
NEXT_ARG();
......@@ -286,7 +287,6 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
return 0;
}
int iplink_parse(int argc, char **argv, struct iplink_req *req,
char **name, char **type, char **link, char **dev, int *group)
{
......@@ -811,7 +811,6 @@ static int set_address(struct ifreq *ifr, int brd)
return 0;
}
static int do_set(int argc, char **argv)
{
char *dev = NULL;
......
......@@ -43,6 +43,9 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
__u32 saddr = 0;
__u32 gaddr = 0;
__u32 daddr = 0;
struct in6_addr saddr6 = IN6ADDR_ANY_INIT;
struct in6_addr gaddr6 = IN6ADDR_ANY_INIT;
struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
unsigned link = 0;
__u8 tos = 0;
__u8 ttl = 0;
......@@ -66,21 +69,30 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
vni_set = 1;
} else if (!matches(*argv, "group")) {
NEXT_ARG();
gaddr = get_addr32(*argv);
if (!IN_MULTICAST(ntohl(gaddr)))
if (!inet_get_addr(*argv, &gaddr, &gaddr6)) {
fprintf(stderr, "Invalid address \"%s\"\n", *argv);
return -1;
}
if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr)))
invarg("invalid group address", *argv);
} else if (!matches(*argv, "remote")) {
NEXT_ARG();
daddr = get_addr32(*argv);
if (IN_MULTICAST(ntohl(daddr)))
if (!inet_get_addr(*argv, &daddr, &daddr6)) {
fprintf(stderr, "Invalid address \"%s\"\n", *argv);
return -1;
}
if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
invarg("invalid remote address", *argv);
} else if (!matches(*argv, "local")) {
NEXT_ARG();
if (strcmp(*argv, "any"))
saddr = get_addr32(*argv);
if (IN_MULTICAST(ntohl(saddr)))
if (strcmp(*argv, "any")) {
if (!inet_get_addr(*argv, &saddr, &saddr6)) {
fprintf(stderr, "Invalid address \"%s\"\n", *argv);
return -1;
}
}
if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6))
invarg("invalid local address", *argv);
} else if (!matches(*argv, "dev")) {
NEXT_ARG();
......@@ -167,7 +179,9 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
fprintf(stderr, "vxlan: missing virtual network identifier\n");
return -1;
}
if (gaddr && daddr) {
if ((gaddr && daddr) ||
(memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) &&
memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) {
fprintf(stderr, "vxlan: both group and remote cannot be specified\n");
return -1;
}
......@@ -176,8 +190,16 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4);
else if (daddr)
addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4);
if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0)
addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr));
else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr));
if (saddr)
addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4);
else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0)
addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr));
if (link)
addattr32(n, 1024, IFLA_VXLAN_LINK, link);
addattr8(n, 1024, IFLA_VXLAN_TTL, ttl);
......@@ -229,6 +251,17 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
fprintf(f, "remote %s ",
format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
}
} else if (tb[IFLA_VXLAN_GROUP6]) {
struct in6_addr addr;
memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr));
if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
if (IN6_IS_ADDR_MULTICAST(&addr))
fprintf(f, "group %s ",
format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
else
fprintf(f, "remote %s ",
format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
}
}
if (tb[IFLA_VXLAN_LOCAL]) {
......@@ -236,6 +269,12 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
if (addr)
fprintf(f, "local %s ",
format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
} else if (tb[IFLA_VXLAN_LOCAL6]) {
struct in6_addr addr;
memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr));
if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0)
fprintf(f, "local %s ",
format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
}
if (tb[IFLA_VXLAN_LINK] &&
......
This diff is collapsed.
......@@ -373,7 +373,7 @@ static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv)
(void *)tmpls_buf, tmpls_len);
}
if (mark.m & mark.v) {
if (mark.m) {
int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
(void *)&mark, sizeof(mark));
if (r < 0) {
......
......@@ -162,7 +162,7 @@ static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type,
if (len > max)
invarg("ALGO-KEYMAT value makes buffer overflow\n", key);
strncpy(buf, key, len);
memcpy(buf, key, len);
}
}
......@@ -528,7 +528,7 @@ static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv)
exit(1);
}
if (mark.m & mark.v) {
if (mark.m) {
int r = addattr_l(&req.n, sizeof(req.buf), XFRMA_MARK,
(void *)&mark, sizeof(mark));
if (r < 0) {
......
......@@ -103,6 +103,7 @@ __PF(IEEE802154, ieee802.15.4)
__PF(PHONET, phonet)
__PF(PHONET_PIPE, phonet_pipe)
__PF(CAIF, caif)
__PF(IP6GRE, gre6)
__PF(NONE, none)
__PF(VOID,void)
......
......@@ -868,3 +868,11 @@ int makeargs(char *line, char *argv[], int maxargs)
return argc;
}
int inet_get_addr(const char *src, __u32 *dst, struct in6_addr *dst6)
{
if (strchr(src, ':'))
return inet_pton(AF_INET6, src, dst6);
else
return inet_pton(AF_INET, src, dst);
}
......@@ -13,7 +13,7 @@ bridge \- show / manipulate bridge addresses and devices
.ti -8
.IR OBJECT " := { "
.BR link " | " fdb " | " vlan " | " monitor " }"
.BR link " | " fdb " | " mdb " | " vlan " | " monitor " }"
.sp
.ti -8
......@@ -64,6 +64,21 @@ bridge \- show / manipulate bridge addresses and devices
.B dev
.IR DEV " ]"
.ti -8
.BR "bridge mdb" " { " add " | " del " } "
.B dev
.IR DEV
.B port
.IR PORT
.B grp
.IR GROUP " [ "
.BR permanent " | " temp " ]"
.ti -8
.BR "bridge mdb show " [ "
.B dev
.IR DEV " ]"
.ti -8
.BR "bridge vlan" " { " add " | " del " } "
.B dev
......@@ -79,7 +94,7 @@ bridge \- show / manipulate bridge addresses and devices
.IR DEV " ]"
.ti -8
.BR "bridge monitor" " [ " all " | " neigh " | " link " ]"
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " ]"
.SH OPTIONS
......@@ -109,6 +124,10 @@ As a rule, the information is statistics or some time values.
.B fdb
- Forwarding Database entry.
.TP
.B mdb
- Multicast group database entry.
.TP
.B vlan
- VLAN filter list.
......@@ -326,6 +345,69 @@ With the
option, the command becomes verbose. It prints out the last updated
and last used time for each entry.
.SH bridge mdb - multicast group database management
.B mdb
objects contain known IP multicast group addresses on a link.
.P
The corresponding commands display mdb entries, add new entries,
and delete old ones.
.SS bridge mdb add - add a new multicast group database entry
This command creates a new mdb entry.
.TP
.BI dev " DEV"
the interface where this group address is associated.
.TP
.BI port " PORT"
the port whose link is known to have members of this multicast group.
.TP
.BI grp " GROUP"
the IP multicast group address whose members reside on the link connected to
the port.
.B permanent
- the mdb entry is permanent
.sp
.B temp
- the mdb entry is temporary (default)
.sp
.in -8
.SS bridge mdb delete - delete a multicast group database entry
This command removes an existing mdb entry.
.PP
The arguments are the same as with
.BR "bridge mdb add" .
.SS bridge mdb show - list multicast group database entries
This command displays the current multicast group membership table. The table
is populated by IGMP and MLD snooping in the bridge driver automatically. It
can be altered by
.B bridge mdb add
and
.B bridge mdb del
commands manually too.
.TP
.BI dev " DEV"
the interface only whose entries should be listed. Default is to list all
bridge interfaces.
.PP
With the
.B -details
option, the command becomes verbose. It prints out the ports known to have
a connected router.
.SH bridge vlan - VLAN filter list
.B vlan
......@@ -395,7 +477,7 @@ command is the first in the command line and then the object list follows:
.I OBJECT-LIST
is the list of object types that we want to monitor.
It may contain
.BR link ", and " fdb "."
.BR link ", " fdb ", and " mdb "."
If no
.B file
argument is given,
......
......@@ -62,7 +62,11 @@ ip-link \- network device configuration
.BR vxlan " |"
.BR ip6tnl " |"
.BR ipip " |"
.BR sit " ]"
.BR sit " |"
.BR gre " |"
.BR gretap " |"
.BR ip6gre " |"
.BR ip6gretap " ]"
.ti -8
.BI "ip link delete " DEVICE
......@@ -186,6 +190,18 @@ Link types:
.sp
.BR sit
- Virtual tunnel interface IPv6 over IPv4
.sp
.BR gre
- Virtual tunnel interface GRE over IPv4
.sp
.BR gretap
- Virtual L2 tuunel interface GRE over IPv4
.sp
.BR ip6gre
- Virtual tuunel interface GRE over IPv6
.sp
.BR ip6gretap
- Virtual L2 tuunel interface GRE over IPv6
.in -8
.TP
......@@ -292,6 +308,112 @@ are entered into the VXLAN device forwarding database.
.in -8
.TP
IP6GRE/IP6GRETAP Type Support
For a link of type
.I IP6GRE/IP6GRETAP
the following additional arguments are supported:
.BI "ip link add " DEVICE
.BI type " { ip6gre | ip6gretap } " remote " ADDR " local " ADDR
.R " [ "
.I "[i|o]seq]"
.R " ] [ "
.I "[i|o]key" KEY
.R " ] [ "
.I " [i|o]csum "
.R " ] [ "
.BI hoplimit " TTL "
.R " ] [ "
.BI encaplimit " ELIM "
.R " ] [ "
.BI tclass " TCLASS "
.R " ] [ "
.BI flowlabel " FLOWLABEL "
.R " ] [ "
.BI "dscp inherit"
.R " ] [ "
.BI dev " PHYS_DEV "
.R " ]"
.in +8
.sp
.BI remote " ADDR "
- specifies the remote IPv6 address of the tunnel.
.sp
.BI local " ADDR "
- specifies the fixed local IPv6 address for tunneled packets.
It must be and address on another interface on this host.
.sp
.BI [i|o]seq
- serialize packets.
The
.B oseq
flag enables sequencing of outgoing packets.
The
.B iseq
flag requires that all input packets are serialized.
.sp
.BI [i|o]key " KEY"
- use keyed GRE with key
.IR KEY ". "KEY
is either a number or an IPv4 address-like dotted quad.
The
.B key
parameter specifies the same key to use in both directions.
The
.BR ikey " and " okey
parameters specify different keys for input and output.
.sp
.BI [i|o]csum
- generate/require checksums for tunneled packets.
The
.B ocsum
flag calculates checksums for outgoing packets.
The
.B icsum
flag requires that all input packets have the correct
checksum. The
.B csum
flag is equivalent to the combination
.BR "icsum ocsum" .
.sp
.BI hoplimit " TTL"
- specifies Hop Limit value to use in outgoing packets.
.sp
.BI encaplimit " ELIM"
- specifies a fixed encapsulation limit. Default is 4.
.sp
.BI flowlabel " FLOWLABEL"
- specifies a fixed flowlabel.
.sp
.BI tclass " TCLASS"
- specifies the traffic class field on
tunneled packets, which can be specified as either a two-digit
hex value (e.g. c0) or a predefined string (e.g. internet).
The value
.B inherit
causes the field to be copied from the original IP header. The
values
.BI "inherit/" STRING
or
.BI "inherit/" 00 ".." ff
will set the field to
.I STRING
or
.IR 00 ".." ff
when tunneling non-IP packets. The default value is 00.
.in -8
.SS ip link delete - delete virtual link
.I DEVICE
specifies the virtual device to act operate on.
......
......@@ -50,7 +50,7 @@ ip-tunnel - tunnel configuration
.ti -8
.IR MODE " := "
.RB " { " ipip " | " gre " | " sit " | " isatap " | " ip6ip6 " | " ipip6 " | " any " }"
.RB " { " ipip " | " gre " | " sit " | " isatap " | " ip6ip6 " | " ipip6 " | " ip6gre " | " any " }"
.ti -8
.IR ADDR " := { " IP_ADDRESS " |"
......@@ -110,7 +110,7 @@ Modes for IPv4 encapsulation available:
.BR ipip ", " sit ", " isatap " and " gre "."
.br
Modes for IPv6 encapsulation available:
.BR ip6ip6 ", " ipip6 " and " any "."
.BR ip6ip6 ", " ipip6 ", " ip6gre ", and " any "."
.TP
.BI remote " ADDRESS"
......
......@@ -33,6 +33,9 @@ Statistics file to use.
.B \-i, \-\-interval <intv>
Set interval to 'intv' seconds.
.TP
.B \-j, \-\-json
Display results in JSON format
.TP
.B \-k, \-\-keys k,k,k,...
Display only keys specified.
.TP
......
......@@ -15,33 +15,35 @@ and
are simple tools to monitor kernel snmp counters and network interface statistics.
.SH OPTIONS
.TP
-h -?
.B \-h, \-\-help
Print help
.TP
-v -V
.B \-V, \-\-version
Print version
.TP
-z
.B \-z, \-\-zero
Dump zero counters too. By default they are not shown.
.TP
-r
.B \-r, \-\-reset
Reset history.
.TP
-n
.B \-n, \-\-nooutput
Do not display anything, only update history.
.TP
-a
.B \-a, \-\-ignore
Dump absolute values of counters. The default is to calculate increments since the previous use.
.TP
-s
.B \-s, \-\-noupdate
Do not update history, so that the next time you will see counters including values accumulated to the moment of this measurement too.
.B \-j, \-\-json
Display results in JSON format.
.TP
-d <INTERVAL>
.B \-d, \-\-interval <INTERVAL>
Run in daemon mode collecting statistics. <INTERVAL> is interval between measurements in seconds.
.TP
-t <INTERVAL>
Time interval to average rates. Default value is 60 seconds.
.TP
.SH SEE ALSO
lnstat(8)
......
......@@ -38,6 +38,7 @@ int dump_zeros = 0;
int reset_history = 0;
int ignore_history = 0;
int no_output = 0;
int json_output = 0;
int no_update = 0;
int scan_interval = 0;
int time_constant = 0;
......@@ -61,6 +62,32 @@ struct ifstat_ent
__u32 ival[MAXS];
};
static const char *stats[MAXS] = {
"rx_packets",
"tx_packets",
"rx_bytes",
"tx_bytes",
"rx_errors",
"tx_errors",
"rx_dropped",
"tx_dropped",
"multicast",
"collisions",
"rx_length_errors",
"rx_over_errors",
"rx_crc_errors",
"rx_frame_errors",
"rx_fifo_errors",
"rx_missed_errors",
"tx_aborted_errors",
"tx_carrier_errors",
"tx_fifo_errors",
"tx_heartbeat_errors",
"tx_window_errors",
"rx_compressed",
"tx_compressed"
};
struct ifstat_ent *kern_db;
struct ifstat_ent *hist_db;
......@@ -212,8 +239,13 @@ static void load_raw_table(FILE *fp)
static void dump_raw_db(FILE *fp, int to_hist)
{
struct ifstat_ent *n, *h;
const char *eol = "\n";
h = hist_db;
fprintf(fp, "#%s\n", info_source);
if (json_output)
fprintf(fp, "{ \"%s\":{", info_source);
else
fprintf(fp, "#%s\n", info_source);
for (n=kern_db; n; n=n->next) {
int i;
......@@ -232,10 +264,22 @@ static void dump_raw_db(FILE *fp, int to_hist)
}
}
}
fprintf(fp, "%d %s ", n->ifindex, n->name);
for (i=0; i<MAXS; i++)
fprintf(fp, "%llu %u ", vals[i], (unsigned)rates[i]);
fprintf(fp, "\n");
if (json_output) {
fprintf(fp, "%s \"%s\":{",
eol, n->name);
eol = ",\n";
for (i=0; i<MAXS && stats[i]; i++)
fprintf(fp, " \"%s\":%llu",
stats[i], vals[i]);
fprintf(fp, "}");
} else {
fprintf(fp, "%d %s ", n->ifindex, n->name);
for (i=0; i<MAXS; i++)
fprintf(fp, "%llu %u ", vals[i],
(unsigned)rates[i]);
fprintf(fp, "\n");
}
}
}
......@@ -244,10 +288,11 @@ static const unsigned long long giga = 1000000000ull;
static const unsigned long long mega = 1000000;
static const unsigned long long kilo = 1000;
static void format_rate(FILE *fp, unsigned long long *vals,
double *rates, int i)
static void format_rate(FILE *fp, const unsigned long long *vals,
const double *rates, int i)
{
char temp[64];
if (vals[i] > giga)
fprintf(fp, "%7lluM ", vals[i]/mega);
else if (vals[i] > mega)
......@@ -265,7 +310,7 @@ static void format_rate(FILE *fp, unsigned long long *vals,
fprintf(fp, "%-6u ", (unsigned)rates[i]);
}
static void format_pair(FILE *fp, unsigned long long *vals, int i, int k)
static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k)
{
char temp[64];
if (vals[i] > giga)
......@@ -328,10 +373,27 @@ static void print_head(FILE *fp)
}
}
static void print_one_if(FILE *fp, struct ifstat_ent *n,
unsigned long long *vals)
static void print_one_json(FILE *fp, const struct ifstat_ent *n,
const unsigned long long *vals)
{
int i, m;
const char *sep = " ";
m = show_errors ? 20 : 10;
fprintf(fp, " \"%s\":{", n->name);
for (i=0; i < m && stats[i]; i++) {
fprintf(fp, "%s\"%s\":%llu",
sep, stats[i], vals[i]);
sep = ", ";
}
fprintf(fp, " }");
}
static void print_one_if(FILE *fp, const struct ifstat_ent *n,
const unsigned long long *vals)
{
int i;
fprintf(fp, "%-15s ", n->name);
for (i=0; i<4; i++)
format_rate(fp, vals, n->rate, i);
......@@ -375,27 +437,42 @@ static void print_one_if(FILE *fp, struct ifstat_ent *n,
}
}
static void dump_kern_db(FILE *fp)
{
struct ifstat_ent *n;
const char *eol = "\n";
print_head(fp);
if (json_output)
fprintf(fp, "{ \"%s\": {", info_source);
else
print_head(fp);
for (n=kern_db; n; n=n->next) {
if (!match(n->name))
continue;
print_one_if(fp, n, n->val);
if (json_output) {
fprintf(fp, "%s", eol);
eol = ",\n";
print_one_json(fp, n, n->val);
} else
print_one_if(fp, n, n->val);
}
if (json_output)
fprintf(fp, "\n} }\n");
}
static void dump_incr_db(FILE *fp)
{
struct ifstat_ent *n, *h;
h = hist_db;
const char *eol = "\n";
print_head(fp);
h = hist_db;
if (json_output)
fprintf(fp, "{ \"%s\":{", info_source);
else
print_head(fp);
for (n=kern_db; n; n=n->next) {
int i;
......@@ -414,8 +491,16 @@ static void dump_incr_db(FILE *fp)
}
if (!match(n->name))
continue;
print_one_if(fp, n, vals);
if (json_output) {
fprintf(fp, "%s", eol);
eol = ",\n";
print_one_json(fp, n, n->val);
} else
print_one_if(fp, n, vals);
}
if (json_output)
fprintf(fp, "\n} }\n");
}
......@@ -559,9 +644,10 @@ static void usage(void)
" -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"
" -r, --reset reset history\n"
" -s, --noupdate don;t update 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");
......@@ -575,6 +661,7 @@ static const struct option longopts[] = {
{ "scan", 1, 0, 'd'},
{ "errors", 0, 0, 'e' },
{ "nooutput", 0, 0, 'n' },
{ "json", 0, 0, 'j' },
{ "reset", 0, 0, 'r' },
{ "noupdate", 0, 0, 's' },
{ "interval", 1, 0, 't' },
......@@ -591,7 +678,7 @@ int main(int argc, char *argv[])
int ch;
int fd;
while ((ch = getopt_long(argc, argv, "hvVzrnasd:t:eK",
while ((ch = getopt_long(argc, argv, "hjvVzrnasd:t:e",
longopts, NULL)) != EOF) {
switch(ch) {
case 'z':
......@@ -612,6 +699,9 @@ int main(int argc, char *argv[])
case 'e':
show_errors = 1;
break;
case 'j':
json_output = 1;
break;
case 'd':
scan_interval = atoi(optarg) * 1000;
if (scan_interval <= 0) {
......@@ -759,11 +849,14 @@ int main(int argc, char *argv[])
else
dump_incr_db(stdout);
}
if (!no_update) {
ftruncate(fileno(hist_fp), 0);
rewind(hist_fp);
json_output = 0;
dump_raw_db(hist_fp, 1);
fflush(hist_fp);
fclose(hist_fp);
}
exit(0);
}
......@@ -41,7 +41,8 @@
static struct option opts[] = {
{ "version", 0, NULL, 'V' },
{ "count", 1, NULL, 'c' },
{ "dump", 1, NULL, 'd' },
{ "dump", 0, NULL, 'd' },
{ "json", 0, NULL, 'j' },
{ "file", 1, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "interval", 1, NULL, 'i' },
......@@ -63,6 +64,8 @@ static int usage(char *name, int exit_code)
"Print <count> number of intervals\n");
fprintf(stderr, "\t-d --dump\t\t"
"Dump list of available files/keys\n");
fprintf(stderr, "\t-j --json\t\t"
"Display in JSON format\n");
fprintf(stderr, "\t-f --file <file>\tStatistics file to use\n");
fprintf(stderr, "\t-h --help\t\tThis help message\n");
fprintf(stderr, "\t-i --interval <intv>\t"
......@@ -94,7 +97,7 @@ static void print_line(FILE *of, const struct lnstat_file *lnstat_files,
int i;
for (i = 0; i < fp->num; i++) {
struct lnstat_field *lf = fp->params[i].lf;
const struct lnstat_field *lf = fp->params[i].lf;
char formatbuf[255];
snprintf(formatbuf, sizeof(formatbuf)-1, "%%%ulu|",
......@@ -104,6 +107,30 @@ static void print_line(FILE *of, const struct lnstat_file *lnstat_files,
fputc('\n', of);
}
static void print_json(FILE *of, const struct lnstat_file *lnstat_files,
const struct field_params *fp)
{
int i;
const char *sep;
const char *base = NULL;
fputs("{\n", of);
for (i = 0; i < fp->num; i++) {
const struct lnstat_field *lf = fp->params[i].lf;
if (!base || lf->file->basename != base) {
if (base) fputs("},\n", of);
base = lf->file->basename;
sep = "\n\t";
fprintf(of, " \"%s\":{", base);
}
fprintf(of, "%s\"%s\":%lu", sep,
lf->name, lf->result);
sep = ",\n\t";
}
fputs("}\n}\n", of);
}
/* find lnstat_field according to user specification */
static int map_field_params(struct lnstat_file *lnstat_files,
struct field_params *fps, int interval)
......@@ -218,15 +245,16 @@ int main(int argc, char **argv)
{
struct lnstat_file *lnstat_files;
const char *basename;
int c;
int i, c;
int interval = DEFAULT_INTERVAL;
int hdr = 2;
enum {
MODE_DUMP,
MODE_JSON,
MODE_NORMAL,
} mode = MODE_NORMAL;
unsigned long count = 1;
struct table_hdr *header;
static struct field_params fp;
int num_req_files = 0;
char *req_files[LNSTAT_MAX_FILES];
......@@ -248,70 +276,73 @@ int main(int argc, char **argv)
num_req_files = 1;
}
while ((c = getopt_long(argc, argv,"Vc:df:h?i:k:s:w:",
while ((c = getopt_long(argc, argv,"Vc:djf:h?i:k:s:w:",
opts, NULL)) != -1) {
int i, len = 0;
int len = 0;
char *tmp, *tok;
switch (c) {
case 'c':
count = strtoul(optarg, NULL, 0);
break;
case 'd':
mode = MODE_DUMP;
break;
case 'f':
req_files[num_req_files++] = strdup(optarg);
case 'c':
count = strtoul(optarg, NULL, 0);
break;
case 'd':
mode = MODE_DUMP;
break;
case 'j':
mode = MODE_JSON;
break;
case 'f':
req_files[num_req_files++] = strdup(optarg);
break;
case '?':
case 'h':
usage(argv[0], 0);
break;
case 'i':
sscanf(optarg, "%u", &interval);
break;
case 'k':
tmp = strdup(optarg);
if (!tmp)
break;
case '?':
case 'h':
usage(argv[0], 0);
break;
case 'i':
sscanf(optarg, "%u", &interval);
break;
case 'k':
tmp = strdup(optarg);
if (!tmp)
for (tok = strtok(tmp, ",");
tok;
tok = strtok(NULL, ",")) {
if (fp.num >= MAX_FIELDS) {
fprintf(stderr,
"WARN: too many keys"
" requested: (%d max)\n",
MAX_FIELDS);
break;
for (tok = strtok(tmp, ",");
tok;
tok = strtok(NULL, ",")) {
if (fp.num >= MAX_FIELDS) {
fprintf(stderr,
"WARN: too many keys"
" requested: (%d max)\n",
MAX_FIELDS);
break;
}
fp.params[fp.num++].name = tok;
}
fp.params[fp.num++].name = tok;
}
break;
case 's':
sscanf(optarg, "%u", &hdr);
break;
case 'w':
tmp = strdup(optarg);
if (!tmp)
break;
case 's':
sscanf(optarg, "%u", &hdr);
break;
case 'w':
tmp = strdup(optarg);
if (!tmp)
break;
i = 0;
for (tok = strtok(tmp, ",");
tok;
tok = strtok(NULL, ",")) {
len = strtoul(tok, NULL, 0);
if (len > FIELD_WIDTH_MAX)
len = FIELD_WIDTH_MAX;
i = 0;
for (tok = strtok(tmp, ",");
tok;
tok = strtok(NULL, ",")) {
len = strtoul(tok, NULL, 0);
if (len > FIELD_WIDTH_MAX)
len = FIELD_WIDTH_MAX;
fp.params[i].print.width = len;
i++;
}
if (i == 1) {
for (i = 0; i < MAX_FIELDS; i++)
fp.params[i].print.width = len;
i++;
}
if (i == 1) {
for (i = 0; i < MAX_FIELDS; i++)
fp.params[i].print.width = len;
}
break;
default:
usage(argv[0], 1);
break;
}
break;
default:
usage(argv[0], 1);
break;
}
}
......@@ -319,13 +350,12 @@ int main(int argc, char **argv)
(const char **) req_files);
switch (mode) {
int i;
struct table_hdr *header;
case MODE_DUMP:
lnstat_dump(stderr, lnstat_files);
break;
case MODE_NORMAL:
case MODE_NORMAL:
case MODE_JSON:
if (!map_field_params(lnstat_files, &fp, interval))
exit(1);
......@@ -334,16 +364,23 @@ int main(int argc, char **argv)
exit(1);
if (interval < 1 )
interval=1;
interval = 1;
for (i = 0; i < count; i++) {
if ((hdr > 1 && (! (i % 20))) || (hdr == 1 && i == 0))
print_hdr(stdout, header);
lnstat_update(lnstat_files);
print_line(stdout, lnstat_files, &fp);
if (mode == MODE_JSON)
print_json(stdout, lnstat_files, &fp);
else {
if ((hdr > 1 &&
(! (i % 20))) || (hdr == 1 && i == 0))
print_hdr(stdout, header);
print_line(stdout, lnstat_files, &fp);
}
fflush(stdout);
sleep(interval);
if (i < count - 1)
sleep(interval);
}
break;
}
return 1;
......
......@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <signal.h>
#include <math.h>
#include <getopt.h>
#include <SNAPSHOT.h>
......@@ -33,6 +34,7 @@ int dump_zeros = 0;
int reset_history = 0;
int ignore_history = 0;
int no_output = 0;
int json_output = 0;
int no_update = 0;
int scan_interval = 0;
int time_constant = 0;
......@@ -255,11 +257,18 @@ static void load_netstat(void)
}
}
static void dump_kern_db(FILE *fp, int to_hist)
{
struct nstat_ent *n, *h;
const char *eol = "\n";
h = hist_db;
fprintf(fp, "#%s\n", info_source);
if (json_output)
fprintf(fp, "{ \"%s\":{", info_source);
else
fprintf(fp, "#%s\n", info_source);
for (n=kern_db; n; n=n->next) {
unsigned long long val = n->val;
if (!dump_zeros && !val && !n->rate)
......@@ -276,15 +285,29 @@ static void dump_kern_db(FILE *fp, int to_hist)
}
}
}
fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate);
if (json_output) {
fprintf(fp, "%s \"%s\":%llu",
eol, n->id, val);
eol = ",\n";
} else
fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate);
}
if (json_output)
fprintf(fp, "\n} }\n");
}
static void dump_incr_db(FILE *fp)
{
struct nstat_ent *n, *h;
const char *eol = "\n";
h = hist_db;
fprintf(fp, "#%s\n", info_source);
if (json_output)
fprintf(fp, "{ \"%s\":{", info_source);
else
fprintf(fp, "#%s\n", info_source);
for (n=kern_db; n; n=n->next) {
int ovfl = 0;
unsigned long long val = n->val;
......@@ -304,9 +327,17 @@ static void dump_incr_db(FILE *fp)
continue;
if (!match(n->id))
continue;
fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val,
n->rate, ovfl?" (overflow)":"");
if (json_output) {
fprintf(fp, "%s \"%s\":%llu",
eol, n->id, val);
eol = ",\n";
} else
fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val,
n->rate, ovfl?" (overflow)":"");
}
if (json_output)
fprintf(fp, "\n} }\n");
}
static int children;
......@@ -437,11 +468,33 @@ static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr,
"Usage: nstat [ -h?vVzrnasd:t: ] [ PATTERN [ PATTERN ] ]\n"
);
"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"
" -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);
}
static const struct option longopts[] = {
{ "help", 0, 0, 'h' },
{ "ignore", 0, 0, 'a' },
{ "scan", 1, 0, 'd'},
{ "nooutput", 0, 0, 'n' },
{ "json", 0, 0, 'j' },
{ "reset", 0, 0, 'r' },
{ "noupdate", 0, 0, 's' },
{ "interval", 1, 0, 't' },
{ "version", 0, 0, 'V' },
{ "zeros", 0, 0, 'z' },
{ 0 }
};
int main(int argc, char *argv[])
{
......@@ -451,7 +504,8 @@ int main(int argc, char *argv[])
int ch;
int fd;
while ((ch = getopt(argc, argv, "h?vVzrnasd:t:")) != EOF) {
while ((ch = getopt_long(argc, argv, "h?vVzrnasd:t:j",
longopts, NULL)) != EOF) {
switch(ch) {
case 'z':
dump_zeros = 1;
......@@ -478,6 +532,9 @@ int main(int argc, char *argv[])
exit(-1);
}
break;
case 'j':
json_output = 1;
break;
case 'v':
case 'V':
printf("nstat utility, iproute2-ss%s\n", SNAPSHOT);
......@@ -614,8 +671,10 @@ int main(int argc, char *argv[])
if (!no_update) {
ftruncate(fileno(hist_fp), 0);
rewind(hist_fp);
json_output = 0;
dump_kern_db(hist_fp, 1);
fflush(hist_fp);
fclose(hist_fp);
}
exit(0);
}
......@@ -38,6 +38,7 @@ TCMODULES += m_nat.o
TCMODULES += m_pedit.o
TCMODULES += m_skbedit.o
TCMODULES += m_csum.o
TCMODULES += m_simple.o
TCMODULES += p_ip.o
TCMODULES += p_icmp.o
TCMODULES += p_tcp.o
......
......@@ -146,7 +146,7 @@ parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct
if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&sel.index, *argv, 10)) {
fprintf(stderr, "Pedit: Illegal \"index\"\n");
fprintf(stderr, "Nat: Illegal \"index\"\n");
return -1;
}
argc--;
......
/*
* m_simple.c simple action
*
* This program is free software; you can distribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: J Hadi Salim <jhs@mojatatu.com>
*
* Pedagogical example. Adds a string that will be printed everytime
* the simple instance is hit.
* Use this as a skeleton action and keep modifying it to meet your needs.
* Look at linux/tc_act/tc_defact.h for the different components ids and
* definitions used in this actions
*
* example use, yell "Incoming ICMP!" every time you see an incoming ICMP on
* eth0. Steps are:
* 1) Add an ingress qdisc point to eth0
* 2) Start a chain on ingress of eth0 that first matches ICMP then invokes
* the simple action to shout.
* 3) display stats and show that no packet has been seen by the action
* 4) Send one ping packet to google (expect to receive a response back)
* 5) grep the logs to see the logged message
* 6) display stats again and observe increment by 1
*
hadi@noma1:$ tc qdisc add dev eth0 ingress
hadi@noma1:$tc filter add dev eth0 parent ffff: protocol ip prio 5 \
u32 match ip protocol 1 0xff flowid 1:1 action simple "Incoming ICMP"
hadi@noma1:$ sudo tc -s filter ls dev eth0 parent ffff:
filter protocol ip pref 5 u32
filter protocol ip pref 5 u32 fh 800: ht divisor 1
filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
match 00010000/00ff0000 at 8
action order 1: Simple <Incoming ICMP>
index 4 ref 1 bind 1 installed 29 sec used 29 sec
Action statistics:
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
hadi@noma1$ ping -c 1 www.google.ca
PING www.google.ca (74.125.225.120) 56(84) bytes of data.
64 bytes from ord08s08-in-f24.1e100.net (74.125.225.120): icmp_req=1 ttl=53 time=31.3 ms
--- www.google.ca ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 31.316/31.316/31.316/0.000 ms
hadi@noma1$ dmesg | grep simple
[135354.473951] simple: Incoming ICMP_1
hadi@noma1$ sudo tc/tc -s filter ls dev eth0 parent ffff:
filter protocol ip pref 5 u32
filter protocol ip pref 5 u32 fh 800: ht divisor 1
filter protocol ip pref 5 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
match 00010000/00ff0000 at 8
action order 1: Simple <Incoming ICMP>
index 4 ref 1 bind 1 installed 206 sec used 67 sec
Action statistics:
Sent 84 bytes 1 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "utils.h"
#include "tc_util.h"
#include <linux/tc_act/tc_defact.h>
#ifndef SIMP_MAX_DATA
#define SIMP_MAX_DATA 32
#endif
static void explain(void)
{
fprintf(stderr, "Usage: ... simple STRING\n"
"STRING being an arbitrary string\n"
"example: \"simple blah\"\n");
}
static void usage(void)
{
explain();
exit(-1);
}
static int
parse_simple(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
struct nlmsghdr *n)
{
struct tc_defact sel = {};
int argc = *argc_p;
char **argv = *argv_p;
int ok = 0;
struct rtattr *tail;
char *simpdata = NULL;
while (argc > 0) {
if (matches(*argv, "simple") == 0) {
NEXT_ARG();
simpdata = *argv;
ok = 1;
argc--;
argv++;
break;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
break;
}
}
if (!ok) {
explain();
return -1;
}
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&sel.index, *argv, 10)) {
fprintf(stderr, "simple: Illegal \"index\"\n");
return -1;
}
argc--;
argv++;
}
}
if (strlen(simpdata) > (SIMP_MAX_DATA - 1)) {
fprintf(stderr, "simple: Illegal string len %ld <%s> \n",
strlen(simpdata), simpdata);
return -1;
}
sel.action = TC_ACT_PIPE;
tail = NLMSG_TAIL(n);
addattr_l(n, MAX_MSG, tca_id, NULL, 0);
addattr_l(n, MAX_MSG, TCA_DEF_PARMS, &sel, sizeof(sel));
addattr_l(n, MAX_MSG, TCA_DEF_DATA, simpdata, SIMP_MAX_DATA);
tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
*argc_p = argc;
*argv_p = argv;
return 0;
}
static int print_simple(struct action_util *au, FILE * f, struct rtattr *arg)
{
struct tc_defact *sel;
struct rtattr *tb[TCA_DEF_MAX + 1];
char *simpdata;
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, TCA_DEF_MAX, arg);
if (tb[TCA_DEF_PARMS] == NULL) {
fprintf(f, "[NULL simple parameters]");
return -1;
}
sel = RTA_DATA(tb[TCA_DEF_PARMS]);
if (tb[TCA_DEF_DATA] == NULL) {
fprintf(f, "[missing simple string]");
return -1;
}
simpdata = RTA_DATA(tb[TCA_DEF_DATA]);
fprintf(f, "Simple <%s>\n", simpdata);
fprintf(f, "\t index %d ref %d bind %d", sel->index,
sel->refcnt, sel->bindcnt);
if (show_stats) {
if (tb[TCA_DEF_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_DEF_TM]);
print_tm(f, tm);
fprintf(f, "\n");
}
}
return 0;
}
struct action_util simple_action_util = {
.id = "simple",
.parse_aopt = parse_simple,
.print_aopt = print_simple,
};
......@@ -53,7 +53,7 @@ static void explain(void)
fprintf(stderr, "Usage: ... fq [ limit PACKETS ] [ flow_limit PACKETS ]\n");
fprintf(stderr, " [ quantum BYTES ] [ initial_quantum BYTES ]\n");
fprintf(stderr, " [ maxrate RATE ] [ buckets NUMBER ]\n");
fprintf(stderr, " [ [no]pacing ]\n");
fprintf(stderr, " [ [no]pacing ]\n");
}
static unsigned int ilog2(unsigned int val)
......
......@@ -31,9 +31,11 @@
static void explain(void)
{
fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\n"
" [direct_qlen P]\n"
" default minor id of class to which unclassified packets are sent {0}\n"
" r2q DRR quantums are computed as rate in Bps/r2q {10}\n"
" debug string of 16 numbers each 0-3 {0}\n\n"
" direct_qlen Limit of the direct queue {in packets}\n"
"... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]\n"
" [prio P] [slot S] [pslot PS]\n"
" [ceil R2] [cburst B2] [mtu MTU] [quantum Q]\n"
......@@ -108,6 +110,7 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
unsigned mtu;
unsigned short mpu = 0;
unsigned short overhead = 0;
unsigned int direct_qlen = ~0U;
unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
struct rtattr *tail;
......@@ -125,6 +128,11 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
if (get_u32(&mtu, *argv, 10)) {
explain1("mtu"); return -1;
}
} else if (matches(*argv, "direct_qlen") == 0) {
NEXT_ARG();
if (get_u32(&direct_qlen, *argv, 10)) {
explain1("direct_qlen"); return -1;
}
} else if (matches(*argv, "mpu") == 0) {
NEXT_ARG();
if (get_u16(&mpu, *argv, 10)) {
......@@ -230,6 +238,9 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
opt.cbuffer = tc_calc_xmittime(opt.ceil.rate, cbuffer);
tail = NLMSG_TAIL(n);
if (direct_qlen != ~0U)
addattr_l(n, 1024, TCA_HTB_DIRECT_QLEN,
&direct_qlen, sizeof(direct_qlen));
addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
addattr_l(n, 2024, TCA_HTB_PARMS, &opt, sizeof(opt));
addattr_l(n, 3024, TCA_HTB_RTAB, rtab, 1024);
......@@ -240,7 +251,7 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str
static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
{
struct rtattr *tb[TCA_HTB_RTAB+1];
struct rtattr *tb[TCA_HTB_MAX + 1];
struct tc_htb_opt *hopt;
struct tc_htb_glob *gopt;
double buffer,cbuffer;
......@@ -253,7 +264,7 @@ static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
if (opt == NULL)
return 0;
parse_rtattr_nested(tb, TCA_HTB_RTAB, opt);
parse_rtattr_nested(tb, TCA_HTB_MAX, opt);
if (tb[TCA_HTB_PARMS]) {
hopt = RTA_DATA(tb[TCA_HTB_PARMS]);
......@@ -302,6 +313,12 @@ static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
if (show_details)
fprintf(f," ver %d.%d",gopt->version >> 16,gopt->version & 0xffff);
}
if (tb[TCA_HTB_DIRECT_QLEN] &&
RTA_PAYLOAD(tb[TCA_HTB_DIRECT_QLEN]) >= sizeof(__u32)) {
__u32 direct_qlen = rta_getattr_u32(tb[TCA_HTB_DIRECT_QLEN]);
fprintf(f, " direct_qlen %u", direct_qlen);
}
return 0;
}
......@@ -329,13 +346,3 @@ struct qdisc_util htb_qdisc_util = {
.parse_copt = htb_parse_class_opt,
.print_copt = htb_print_opt,
};
/* for testing of old one */
struct qdisc_util htb2_qdisc_util = {
.id = "htb2",
.parse_qopt = htb_parse_opt,
.print_qopt = htb_print_opt,
.print_xstats = htb_print_xstats,
.parse_copt = htb_parse_class_opt,
.print_copt = htb_print_opt,
};
......@@ -241,6 +241,9 @@ static int tc_class_list(int argc, char **argv)
t.tcm_family = AF_UNSPEC;
memset(d, 0, sizeof(d));
filter_qdisc = 0;
filter_classid = 0;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
......
......@@ -137,15 +137,16 @@ static int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
if (est.ewma_log)
addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
if (argc) {
if (q) {
if (!q->parse_qopt) {
fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
return -1;
}
if (q) {
if (q->parse_qopt) {
if (q->parse_qopt(q, argc, argv, &req.n))
return 1;
} else {
} else if (argc) {
fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
return -1;
}
} else {
if (argc) {
if (matches(*argv, "help") == 0)
usage();
......
......@@ -171,20 +171,24 @@ int get_rate(unsigned *rate, const char *str)
return 0;
}
void print_rate(char *buf, int len, __u32 rate)
void print_rate(char *buf, int len, __u64 rate)
{
double tmp = (double)rate*8;
extern int use_iec;
if (use_iec) {
if (tmp >= 1000.0*1024.0*1024.0)
if (tmp >= 1000.0*1024.0*1024.0*1024.0)
snprintf(buf, len, "%.0fGibit", tmp/(1024.0*1024.0*1024.0));
else if (tmp >= 1000.0*1024.0*1024.0)
snprintf(buf, len, "%.0fMibit", tmp/(1024.0*1024.0));
else if (tmp >= 1000.0*1024)
snprintf(buf, len, "%.0fKibit", tmp/1024);
else
snprintf(buf, len, "%.0fbit", tmp);
} else {
if (tmp >= 1000.0*1000000.0)
if (tmp >= 1000.0*1000000000.0)
snprintf(buf, len, "%.0fGbit", tmp/1000000000.0);
else if (tmp >= 1000.0*1000000.0)
snprintf(buf, len, "%.0fMbit", tmp/1000000.0);
else if (tmp >= 1000.0 * 1000.0)
snprintf(buf, len, "%.0fKbit", tmp/1000.0);
......@@ -193,7 +197,7 @@ void print_rate(char *buf, int len, __u32 rate)
}
}
char * sprint_rate(__u32 rate, char *buf)
char * sprint_rate(__u64 rate, char *buf)
{
print_rate(buf, SPRINT_BSIZE-1, rate);
return buf;
......@@ -460,9 +464,19 @@ void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtat
q.drops, q.overlimits, q.requeues);
}
if (tbs[TCA_STATS_RATE_EST]) {
if (tbs[TCA_STATS_RATE_EST64]) {
struct gnet_stats_rate_est64 re = {0};
memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST64]),
MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST64]),
sizeof(re)));
fprintf(fp, "\n%srate %s %llupps ",
prefix, sprint_rate(re.bps, b1), re.pps);
} else if (tbs[TCA_STATS_RATE_EST]) {
struct gnet_stats_rate_est re = {0};
memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re)));
memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]),
MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re)));
fprintf(fp, "\n%srate %s %upps ",
prefix, sprint_rate(re.bps, b1), re.pps);
}
......
......@@ -63,12 +63,12 @@ extern int get_size_and_cell(unsigned *size, int *cell_log, char *str);
extern int get_time(unsigned *time, const char *str);
extern int get_linklayer(unsigned *val, const char *arg);
extern void print_rate(char *buf, int len, __u32 rate);
extern void print_rate(char *buf, int len, __u64 rate);
extern void print_size(char *buf, int len, __u32 size);
extern void print_qdisc_handle(char *buf, int len, __u32 h);
extern void print_time(char *buf, int len, __u32 time);
extern void print_linklayer(char *buf, int len, unsigned linklayer);
extern char * sprint_rate(__u32 rate, char *buf);
extern char * sprint_rate(__u64 rate, char *buf);
extern char * sprint_size(__u32 size, char *buf);
extern char * sprint_qdisc_handle(__u32 h, char *buf);
extern char * sprint_tc_classid(__u32 h, char *buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment