Merge branch 'master' into net-next-3.11
Conflicts: tc/q_fq.c
This commit is contained in:
commit
f1f1aeb2ad
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
#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
|
||||
|
|
|
|||
131
ip/ip6tunnel.c
131
ip/ip6tunnel.c
|
|
@ -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] &&
|
||||
|
|
|
|||
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* link_gre6.c gre driver module
|
||||
*
|
||||
* This program 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 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Authors: Dmitry Kozlov <xeb@mail.ru>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <linux/ip.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/ip6_tunnel.h>
|
||||
|
||||
#include "rt_names.h"
|
||||
#include "utils.h"
|
||||
#include "ip_common.h"
|
||||
#include "tunnel.h"
|
||||
|
||||
#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000)
|
||||
#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
|
||||
|
||||
#define DEFAULT_TNL_HOP_LIMIT (64)
|
||||
|
||||
static void usage(void) __attribute__((noreturn));
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
|
||||
fprintf(stderr, " type { ip6gre | ip6gretap } [ remote ADDR ] [ local ADDR ]\n");
|
||||
fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
|
||||
fprintf(stderr, " [ hoplimit TTL ] [ encaplimit ELIM ]\n");
|
||||
fprintf(stderr, " [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
|
||||
fprintf(stderr, " [ dscp inherit ] [ dev PHYS_DEV ]\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Where: NAME := STRING\n");
|
||||
fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
|
||||
fprintf(stderr, " TTL := { 0..255 } (default=%d)\n",
|
||||
DEFAULT_TNL_HOP_LIMIT);
|
||||
fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n");
|
||||
fprintf(stderr, " ELIM := { none | 0..255 }(default=%d)\n",
|
||||
IPV6_DEFAULT_TNL_ENCAP_LIMIT);
|
||||
fprintf(stderr, " TCLASS := { 0x0..0xff | inherit }\n");
|
||||
fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff | inherit }\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||
struct nlmsghdr *n)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg i;
|
||||
char buf[1024];
|
||||
} req;
|
||||
struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
|
||||
struct rtattr *tb[IFLA_MAX + 1];
|
||||
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
|
||||
struct rtattr *greinfo[IFLA_GRE_MAX + 1];
|
||||
__u16 iflags = 0;
|
||||
__u16 oflags = 0;
|
||||
unsigned ikey = 0;
|
||||
unsigned okey = 0;
|
||||
struct in6_addr raddr = IN6ADDR_ANY_INIT;
|
||||
struct in6_addr laddr = IN6ADDR_ANY_INIT;
|
||||
unsigned link = 0;
|
||||
unsigned flowinfo = 0;
|
||||
unsigned flags = 0;
|
||||
__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
|
||||
__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
|
||||
int len;
|
||||
|
||||
if (!(n->nlmsg_flags & NLM_F_CREATE)) {
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
req.n.nlmsg_type = RTM_GETLINK;
|
||||
req.i.ifi_family = preferred_family;
|
||||
req.i.ifi_index = ifi->ifi_index;
|
||||
|
||||
if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
|
||||
get_failed:
|
||||
fprintf(stderr,
|
||||
"Failed to get existing tunnel info.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = req.n.nlmsg_len;
|
||||
len -= NLMSG_LENGTH(sizeof(*ifi));
|
||||
if (len < 0)
|
||||
goto get_failed;
|
||||
|
||||
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
|
||||
|
||||
if (!tb[IFLA_LINKINFO])
|
||||
goto get_failed;
|
||||
|
||||
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
|
||||
|
||||
if (!linkinfo[IFLA_INFO_DATA])
|
||||
goto get_failed;
|
||||
|
||||
parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
|
||||
linkinfo[IFLA_INFO_DATA]);
|
||||
|
||||
if (greinfo[IFLA_GRE_IKEY])
|
||||
ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
|
||||
|
||||
if (greinfo[IFLA_GRE_OKEY])
|
||||
okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
|
||||
|
||||
if (greinfo[IFLA_GRE_IFLAGS])
|
||||
iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
|
||||
|
||||
if (greinfo[IFLA_GRE_OFLAGS])
|
||||
oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
|
||||
|
||||
if (greinfo[IFLA_GRE_LOCAL])
|
||||
memcpy(&laddr, RTA_DATA(greinfo[IFLA_GRE_LOCAL]), sizeof(laddr));
|
||||
|
||||
if (greinfo[IFLA_GRE_REMOTE])
|
||||
memcpy(&raddr, RTA_DATA(greinfo[IFLA_GRE_REMOTE]), sizeof(raddr));
|
||||
|
||||
if (greinfo[IFLA_GRE_TTL])
|
||||
hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
|
||||
|
||||
if (greinfo[IFLA_GRE_LINK])
|
||||
link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
|
||||
|
||||
if (greinfo[IFLA_GRE_ENCAP_LIMIT])
|
||||
encap_limit = rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]);
|
||||
|
||||
if (greinfo[IFLA_GRE_FLOWINFO])
|
||||
flowinfo = rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]);
|
||||
|
||||
if (greinfo[IFLA_GRE_FLAGS])
|
||||
flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
|
||||
}
|
||||
|
||||
while (argc > 0) {
|
||||
if (!matches(*argv, "key")) {
|
||||
unsigned uval;
|
||||
|
||||
NEXT_ARG();
|
||||
iflags |= GRE_KEY;
|
||||
oflags |= GRE_KEY;
|
||||
if (strchr(*argv, '.'))
|
||||
uval = get_addr32(*argv);
|
||||
else {
|
||||
if (get_unsigned(&uval, *argv, 0) < 0) {
|
||||
fprintf(stderr,
|
||||
"Invalid value for \"key\"\n");
|
||||
exit(-1);
|
||||
}
|
||||
uval = htonl(uval);
|
||||
}
|
||||
|
||||
ikey = okey = uval;
|
||||
} else if (!matches(*argv, "ikey")) {
|
||||
unsigned uval;
|
||||
|
||||
NEXT_ARG();
|
||||
iflags |= GRE_KEY;
|
||||
if (strchr(*argv, '.'))
|
||||
uval = get_addr32(*argv);
|
||||
else {
|
||||
if (get_unsigned(&uval, *argv, 0)<0) {
|
||||
fprintf(stderr, "invalid value of \"ikey\"\n");
|
||||
exit(-1);
|
||||
}
|
||||
uval = htonl(uval);
|
||||
}
|
||||
ikey = uval;
|
||||
} else if (!matches(*argv, "okey")) {
|
||||
unsigned uval;
|
||||
|
||||
NEXT_ARG();
|
||||
oflags |= GRE_KEY;
|
||||
if (strchr(*argv, '.'))
|
||||
uval = get_addr32(*argv);
|
||||
else {
|
||||
if (get_unsigned(&uval, *argv, 0)<0) {
|
||||
fprintf(stderr, "invalid value of \"okey\"\n");
|
||||
exit(-1);
|
||||
}
|
||||
uval = htonl(uval);
|
||||
}
|
||||
okey = uval;
|
||||
} else if (!matches(*argv, "seq")) {
|
||||
iflags |= GRE_SEQ;
|
||||
oflags |= GRE_SEQ;
|
||||
} else if (!matches(*argv, "iseq")) {
|
||||
iflags |= GRE_SEQ;
|
||||
} else if (!matches(*argv, "oseq")) {
|
||||
oflags |= GRE_SEQ;
|
||||
} else if (!matches(*argv, "csum")) {
|
||||
iflags |= GRE_CSUM;
|
||||
oflags |= GRE_CSUM;
|
||||
} else if (!matches(*argv, "icsum")) {
|
||||
iflags |= GRE_CSUM;
|
||||
} else if (!matches(*argv, "ocsum")) {
|
||||
oflags |= GRE_CSUM;
|
||||
} else if (!matches(*argv, "remote")) {
|
||||
inet_prefix addr;
|
||||
NEXT_ARG();
|
||||
get_prefix(&addr, *argv, preferred_family);
|
||||
if (addr.family == AF_UNSPEC)
|
||||
invarg("\"remote\" address family is AF_UNSPEC", *argv);
|
||||
memcpy(&raddr, &addr.data, sizeof(raddr));
|
||||
} else if (!matches(*argv, "local")) {
|
||||
inet_prefix addr;
|
||||
NEXT_ARG();
|
||||
get_prefix(&addr, *argv, preferred_family);
|
||||
if (addr.family == AF_UNSPEC)
|
||||
invarg("\"local\" address family is AF_UNSPEC", *argv);
|
||||
memcpy(&laddr, &addr.data, sizeof(laddr));
|
||||
} else if (!matches(*argv, "dev")) {
|
||||
NEXT_ARG();
|
||||
link = if_nametoindex(*argv);
|
||||
if (link == 0)
|
||||
exit(-1);
|
||||
} else if (!matches(*argv, "ttl") ||
|
||||
!matches(*argv, "hoplimit")) {
|
||||
__u8 uval;
|
||||
NEXT_ARG();
|
||||
if (get_u8(&uval, *argv, 0))
|
||||
invarg("invalid TTL", *argv);
|
||||
hop_limit = uval;
|
||||
} else if (!matches(*argv, "tos") ||
|
||||
!matches(*argv, "tclass") ||
|
||||
!matches(*argv, "dsfield")) {
|
||||
__u8 uval;
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "inherit") == 0)
|
||||
flags |= IP6_TNL_F_USE_ORIG_TCLASS;
|
||||
else {
|
||||
if (get_u8(&uval, *argv, 16))
|
||||
invarg("invalid TClass", *argv);
|
||||
flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
|
||||
flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
|
||||
}
|
||||
} else if (strcmp(*argv, "flowlabel") == 0 ||
|
||||
strcmp(*argv, "fl") == 0) {
|
||||
__u32 uval;
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "inherit") == 0)
|
||||
flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
|
||||
else {
|
||||
if (get_u32(&uval, *argv, 16))
|
||||
invarg("invalid Flowlabel", *argv);
|
||||
if (uval > 0xFFFFF)
|
||||
invarg("invalid Flowlabel", *argv);
|
||||
flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
|
||||
flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
|
||||
}
|
||||
} else if (strcmp(*argv, "dscp") == 0) {
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "inherit") != 0)
|
||||
invarg("not inherit", *argv);
|
||||
flags |= IP6_TNL_F_RCV_DSCP_COPY;
|
||||
} else
|
||||
usage();
|
||||
argc--; argv++;
|
||||
}
|
||||
|
||||
addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
|
||||
addattr32(n, 1024, IFLA_GRE_OKEY, okey);
|
||||
addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
|
||||
addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
|
||||
addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
|
||||
addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
|
||||
if (link)
|
||||
addattr32(n, 1024, IFLA_GRE_LINK, link);
|
||||
addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
|
||||
addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
|
||||
addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
|
||||
addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||
{
|
||||
char s1[1024];
|
||||
char s2[64];
|
||||
const char *local = "any";
|
||||
const char *remote = "any";
|
||||
unsigned iflags = 0;
|
||||
unsigned oflags = 0;
|
||||
unsigned flags = 0;
|
||||
unsigned flowinfo = 0;
|
||||
struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
|
||||
|
||||
if (!tb)
|
||||
return;
|
||||
|
||||
if (tb[IFLA_GRE_FLAGS])
|
||||
flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
|
||||
|
||||
if (tb[IFLA_GRE_FLOWINFO])
|
||||
flags = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
|
||||
|
||||
if (tb[IFLA_GRE_REMOTE]) {
|
||||
struct in6_addr addr;
|
||||
memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]), sizeof(addr));
|
||||
|
||||
if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
|
||||
remote = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
|
||||
}
|
||||
|
||||
fprintf(f, "remote %s ", remote);
|
||||
|
||||
if (tb[IFLA_GRE_LOCAL]) {
|
||||
struct in6_addr addr;
|
||||
memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]), sizeof(addr));
|
||||
|
||||
if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
|
||||
local = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
|
||||
}
|
||||
|
||||
fprintf(f, "local %s ", local);
|
||||
|
||||
if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
|
||||
unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
|
||||
const char *n = if_indextoname(link, s2);
|
||||
|
||||
if (n)
|
||||
fprintf(f, "dev %s ", n);
|
||||
else
|
||||
fprintf(f, "dev %u ", link);
|
||||
}
|
||||
|
||||
if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
|
||||
fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
|
||||
|
||||
if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
|
||||
fprintf(f, "encaplimit none ");
|
||||
else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
|
||||
int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
|
||||
|
||||
fprintf(f, "encaplimit %d ", encap_limit);
|
||||
}
|
||||
|
||||
if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
|
||||
fprintf(f, "flowlabel inherit ");
|
||||
else
|
||||
fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
|
||||
|
||||
if (flags & IP6_TNL_F_RCV_DSCP_COPY)
|
||||
fprintf(f, "dscp inherit ");
|
||||
|
||||
if (tb[IFLA_GRE_IFLAGS])
|
||||
iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
|
||||
|
||||
if (tb[IFLA_GRE_OFLAGS])
|
||||
oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
|
||||
|
||||
if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
|
||||
inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
|
||||
fprintf(f, "ikey %s ", s2);
|
||||
}
|
||||
|
||||
if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
|
||||
inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
|
||||
fprintf(f, "okey %s ", s2);
|
||||
}
|
||||
|
||||
if (iflags & GRE_SEQ)
|
||||
fputs("iseq ", f);
|
||||
if (oflags & GRE_SEQ)
|
||||
fputs("oseq ", f);
|
||||
if (iflags & GRE_CSUM)
|
||||
fputs("icsum ", f);
|
||||
if (oflags & GRE_CSUM)
|
||||
fputs("ocsum ", f);
|
||||
}
|
||||
|
||||
struct link_util ip6gre_link_util = {
|
||||
.id = "ip6gre",
|
||||
.maxattr = IFLA_GRE_MAX,
|
||||
.parse_opt = gre_parse_opt,
|
||||
.print_opt = gre_print_opt,
|
||||
};
|
||||
|
||||
struct link_util ip6gretap_link_util = {
|
||||
.id = "ip6gretap",
|
||||
.maxattr = IFLA_GRE_MAX,
|
||||
.parse_opt = gre_parse_opt,
|
||||
.print_opt = gre_print_opt,
|
||||
};
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
131
misc/ifstat.c
131
misc/ifstat.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
171
misc/lnstat.c
171
misc/lnstat.c
|
|
@ -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);
|
||||
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 'd':
|
||||
mode = MODE_DUMP;
|
||||
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)
|
||||
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;
|
||||
|
|
|
|||
77
misc/nstat.c
77
misc/nstat.c
|
|
@ -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--;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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
tc/q_htb.c
31
tc/q_htb.c
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
26
tc/tc_util.c
26
tc/tc_util.c
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue