diff --git a/Makefile b/Makefile index c03d74c9..d1ace1f7 100644 --- a/Makefile +++ b/Makefile @@ -33,11 +33,10 @@ CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall CFLAGS = $(CCOPTS) -I../include $(DEFINES) YACCFLAGS = -d -t -v -LDLIBS += -L../lib -lnetlink -lutil - SUBDIRS=lib ip tc misc netem genl LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a +LDLIBS += $(LIBNETLINK) all: Config @set -e; \ diff --git a/README b/README index 7d54bd2d..0ed03729 100644 --- a/README +++ b/README @@ -1,15 +1,17 @@ -Primary site is: - http://developer.osdl.org/dev/iproute2 +This is a set of utilities for Linux networking. -Original FTP site is: - ftp://ftp.inr.ac.ru/ip-routing/ +Information: + http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 + +Download: + http://devresources.linuxfoundation.org/dev/iproute2/download + +Repository: + git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git How to compile this. -------------------- -1. Look at start of Makefile and set correct values for: - -KERNEL_INCLUDE should point to correct linux kernel include directory. -Default (/usr/src/linux/include) is right as rule. +1. libdbm arpd needs to have the db4 development libraries. For debian users this is the package with a name like libdb4.x-dev. @@ -29,8 +31,13 @@ contains whether or not ATM is available, etc. and make there. It assumes, that latex, dvips and psnup are in your path. +4. This package includes matching sanitized kernel headers because + the build environment may not have up to date versions. See Makefile + if you have special requirements and need to point at different + kernel include files. + Stephen Hemminger -shemminger@osdl.org +shemminger@linux-foundation.org Alexey Kuznetsov kuznet@ms2.inr.ac.ru diff --git a/README.devel b/README.devel new file mode 100644 index 00000000..2022869a --- /dev/null +++ b/README.devel @@ -0,0 +1,15 @@ +Iproute2 development is closely tied to Linux kernel networking +development. Most new features require a kernel and a utility component. + +Please submit both the the Linux networking mailing list + + +The current source is in the git repository: + git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git + +The master branch contains the source corresponding to the current +code in the mainline Linux kernel (ie follows Linus). The net-next +branch is a temporary branch that tracks the code intended for the +next release; it corresponds with networking development branch in +the kernel. + diff --git a/doc/Makefile b/doc/Makefile index 84836a28..1df60813 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -15,6 +15,7 @@ PAGESPERPAGE=2 HTMLFILES=$(subst .sgml,.html,$(shell echo *.sgml)) DVIFILES=$(subst .ps,.dvi,$(PSFILES)) +PDFFILES=$(subst .ps,.pdf,$(PSFILES)) all: pstwocol @@ -25,6 +26,8 @@ html: $(HTMLFILES) dvi: $(DVIFILES) +pdf: $(PDFFILES) + print: $(PSFILES) $(LPR) $(PSFILES) @@ -41,6 +44,11 @@ print: $(PSFILES) echo "Re-running LaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \ done +#%.pdf: %.tex +# pdflatex $< +%.pdf: %.ps + ps2pdf $< + %.ps: %.dvi $(DVIPS) $< -o $@ diff --git a/genl/Makefile b/genl/Makefile index 44bb83cd..03d1f26a 100644 --- a/genl/Makefile +++ b/genl/Makefile @@ -3,6 +3,8 @@ GENLOBJ=genl.o include ../Config SHARED_LIBS ?= y +CFLAGS += -fno-strict-aliasing + GENLMODULES := GENLMODULES += ctrl.o diff --git a/include/utils.h b/include/utils.h index ee09ee56..47f8e07d 100644 --- a/include/utils.h +++ b/include/utils.h @@ -151,5 +151,6 @@ extern int makeargs(char *line, char *argv[], int maxargs); struct iplink_req; int iplink_parse(int argc, char **argv, struct iplink_req *req, - char **name, char **type, char **link, char **dev); + char **name, char **type, char **link, char **dev, + int *group); #endif /* __UTILS_H__ */ diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c index f0db5aa0..b08723a8 100644 --- a/ip/ip6tunnel.c +++ b/ip/ip6tunnel.c @@ -262,8 +262,11 @@ static int do_tunnels_list(struct ip6_tnl_parm *p) } /* skip two lines at the begenning of the file */ - fgets(buf, sizeof(buf), fp); - fgets(buf, sizeof(buf), fp); + if (!fgets(buf, sizeof(buf), fp) || + !fgets(buf, sizeof(buf), fp)) { + fprintf(stderr, "/proc/net/dev read error\n"); + return -1; + } while (fgets(buf, sizeof(buf), fp) != NULL) { char name[IFNAMSIZ]; diff --git a/ip/ipaddress.c b/ip/ipaddress.c index e4748e3d..a1f78b94 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -278,12 +278,10 @@ int print_linkinfo(const struct sockaddr_nl *who, fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); if (tb[IFLA_QDISC]) fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); -#ifdef IFLA_MASTER if (tb[IFLA_MASTER]) { SPRINT_BUF(b1); fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); } -#endif if (tb[IFLA_OPERSTATE]) print_operstate(fp, *(__u8 *)RTA_DATA(tb[IFLA_OPERSTATE])); diff --git a/ip/iplink.c b/ip/iplink.c index 97a960ba..48c02548 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -51,7 +51,7 @@ void iplink_usage(void) fprintf(stderr, " type TYPE [ ARGS ]\n"); fprintf(stderr, " ip link delete DEV type TYPE [ ARGS ]\n"); fprintf(stderr, "\n"); - fprintf(stderr, " ip link set DEVICE [ { up | down } ]\n"); + fprintf(stderr, " ip link set { dev DEVICE | group DEVGROUP } [ { up | down } ]\n"); } else fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n"); @@ -71,6 +71,8 @@ void iplink_usage(void) fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n"); fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n"); fprintf(stderr, " [ rate TXRATE ] ] \n"); + fprintf(stderr, " [ master DEVICE ]\n"); + fprintf(stderr, " [ nomaster ]\n"); fprintf(stderr, " ip link show [ DEVICE | group GROUP ]\n"); if (iplink_have_newlink()) { @@ -244,7 +246,7 @@ int iplink_parse_vf(int vf, int *argcp, char ***argvp, int iplink_parse(int argc, char **argv, struct iplink_req *req, - char **name, char **type, char **link, char **dev) + char **name, char **type, char **link, char **dev, int *group) { int ret, len; char abuf[32]; @@ -253,6 +255,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, int netns = -1; int vf = -1; + *group = -1; ret = argc; while (argc > 0) { @@ -361,7 +364,18 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, if (len < 0) return -1; addattr_nest_end(&req->n, vflist); -#ifdef IFF_DYNAMIC + } else if (matches(*argv, "master") == 0) { + int ifindex; + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, 4); + } else if (matches(*argv, "nomaster") == 0) { + int ifindex = 0; + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, 4); } else if (matches(*argv, "dynamic") == 0) { NEXT_ARG(); req->i.ifi_change |= IFF_DYNAMIC; @@ -371,7 +385,6 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, req->i.ifi_flags &= ~IFF_DYNAMIC; } else return on_off("dynamic"); -#endif } else if (matches(*argv, "type") == 0) { NEXT_ARG(); *type = *argv; @@ -383,6 +396,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, *argv, strlen(*argv)); argc--; argv++; break; + } else if (strcmp(*argv, "group") == 0) { + NEXT_ARG(); + if (*group != -1) + duparg("group", *argv); + if (rtnl_group_a2n(group, *argv)) + invarg("Invalid \"group\" value\n", *argv); } else { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -406,6 +425,7 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) char *name = NULL; char *link = NULL; char *type = NULL; + int group; struct link_util *lu = NULL; struct iplink_req req; int ret; @@ -417,12 +437,38 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) req.n.nlmsg_type = cmd; req.i.ifi_family = preferred_family; - ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev); + ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group); if (ret < 0) return ret; argc -= ret; argv += ret; + + if (group != -1) { + if (dev) + addattr_l(&req.n, sizeof(req), IFLA_GROUP, + &group, sizeof(group)); + else { + if (argc) { + fprintf(stderr, "Garbage instead of arguments " + "\"%s ...\". Try \"ip link " + "help\".\n", *argv); + return -1; + } + if (flags & NLM_F_CREATE) { + fprintf(stderr, "group cannot be used when " + "creating devices.\n"); + return -1; + } + + req.i.ifi_index = 0; + addattr32(&req.n, sizeof(req), IFLA_GROUP, group); + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + return 0; + } + } + ll_init_map(&rth); if (type) { @@ -779,7 +825,6 @@ static int do_set(int argc, char **argv) flags |= IFF_NOARP; } else return on_off("noarp"); -#ifdef IFF_DYNAMIC } else if (matches(*argv, "dynamic") == 0) { NEXT_ARG(); mask |= IFF_DYNAMIC; @@ -789,7 +834,6 @@ static int do_set(int argc, char **argv) flags &= ~IFF_DYNAMIC; } else return on_off("dynamic"); -#endif } else { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c index 44ffdfcf..5a52c856 100644 --- a/ip/ipmaddr.c +++ b/ip/ipmaddr.c @@ -128,14 +128,15 @@ void read_igmp(struct ma_info **result_p) if (!fp) return; memset(&m, 0, sizeof(m)); - fgets(buf, sizeof(buf), fp); + if (!fgets(buf, sizeof(buf), fp)) + return; m.addr.family = AF_INET; m.addr.bitlen = 32; m.addr.bytelen = 4; while (fgets(buf, sizeof(buf), fp)) { - struct ma_info *ma = malloc(sizeof(m)); + struct ma_info *ma; if (buf[0] != '\t') { sscanf(buf, "%d%s", &m.index, m.name); diff --git a/ip/ipmroute.c b/ip/ipmroute.c index 977143cc..a4389f57 100644 --- a/ip/ipmroute.c +++ b/ip/ipmroute.c @@ -58,7 +58,8 @@ static void read_viftable(void) if (!fp) return; - fgets(buf, sizeof(buf), fp); + if (!fgets(buf, sizeof(buf), fp)) + return; while (fgets(buf, sizeof(buf), fp)) { int vifi; @@ -83,7 +84,8 @@ static void read_mroute_list(FILE *ofp) if (!fp) return; - fgets(buf, sizeof(buf), fp); + if (!fgets(buf, sizeof(buf), fp)) + return; while (fgets(buf, sizeof(buf), fp)) { inet_prefix maddr, msrc; diff --git a/ip/ipneigh.c b/ip/ipneigh.c index 2d96ab7c..dff9eb46 100644 --- a/ip/ipneigh.c +++ b/ip/ipneigh.c @@ -277,12 +277,10 @@ int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) ci->ndm_confirmed/hz, ci->ndm_updated/hz); } -#ifdef NDA_PROBES if (tb[NDA_PROBES] && show_stats) { __u32 p = *(__u32 *) RTA_DATA(tb[NDA_PROBES]); fprintf(fp, " probes %u", p); } -#endif if (r->ndm_state) { int nud = r->ndm_state; diff --git a/ip/iproute.c b/ip/iproute.c index 06d2507c..2fef35b7 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -478,12 +478,11 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (ci->rta_lastuse != 0) fprintf(fp, " age %dsec", ci->rta_lastuse/hz); } -#ifdef RTNETLINK_HAVE_PEERINFO if (ci->rta_id) fprintf(fp, " ipid 0x%04x", ci->rta_id); if (ci->rta_ts || ci->rta_tsage) - fprintf(fp, " ts 0x%x tsage %dsec", ci->rta_ts, ci->rta_tsage); -#endif + fprintf(fp, " ts 0x%x tsage %dsec", + ci->rta_ts, ci->rta_tsage); } } else if (r->rtm_family == AF_INET6) { struct rta_cacheinfo *ci = NULL; @@ -792,7 +791,6 @@ int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (get_unsigned(&mtu, *argv, 0)) invarg("\"mtu\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); -#ifdef RTAX_HOPLIMIT } else if (strcmp(*argv, "hoplimit") == 0) { unsigned hoplimit; NEXT_ARG(); @@ -803,8 +801,6 @@ int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (get_unsigned(&hoplimit, *argv, 0)) invarg("\"hoplimit\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit); -#endif -#ifdef RTAX_ADVMSS } else if (strcmp(*argv, "advmss") == 0) { unsigned mss; NEXT_ARG(); @@ -815,8 +811,6 @@ int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (get_unsigned(&mss, *argv, 0)) invarg("\"mss\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss); -#endif -#ifdef RTAX_REORDERING } else if (matches(*argv, "reordering") == 0) { unsigned reord; NEXT_ARG(); @@ -827,7 +821,6 @@ int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (get_unsigned(&reord, *argv, 0)) invarg("\"reordering\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord); -#endif } else if (strcmp(*argv, "rtt") == 0) { unsigned rtt; NEXT_ARG(); diff --git a/ip/iptunnel.c b/ip/iptunnel.c index fe5e3f96..f038f0a6 100644 --- a/ip/iptunnel.c +++ b/ip/iptunnel.c @@ -407,8 +407,12 @@ static int do_tunnels_list(struct ip_tunnel_parm *p) return -1; } - fgets(buf, sizeof(buf), fp); - fgets(buf, sizeof(buf), fp); + /* skip header lines */ + if (!fgets(buf, sizeof(buf), fp) || + !fgets(buf, sizeof(buf), fp)) { + fprintf(stderr, "/proc/net/dev read error\n"); + return -1; + } while (fgets(buf, sizeof(buf), fp) != NULL) { int index, type; diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c index a276c0b8..48a732f0 100644 --- a/ip/ipxfrm.c +++ b/ip/ipxfrm.c @@ -854,6 +854,7 @@ void xfrm_state_info_print(struct xfrm_usersa_info *xsinfo, XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_WILDRECV, "wildrecv"); XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ICMP, "icmp"); XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_AF_UNSPEC, "af-unspec"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_ALIGN4, "align4"); if (flags) fprintf(fp, "%x", flags); } @@ -980,6 +981,7 @@ void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo, fprintf(fp, "flag "); XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_LOCALOK, "localok"); + XFRM_FLAG_PRINT(fp, flags, XFRM_POLICY_ICMP, "icmp"); if (flags) fprintf(fp, "%x", flags); } diff --git a/ip/link_veth.c b/ip/link_veth.c index 9f5e8715..3d19b01f 100644 --- a/ip/link_veth.c +++ b/ip/link_veth.c @@ -30,6 +30,7 @@ static int veth_parse_opt(struct link_util *lu, int argc, char **argv, char *name, *type, *link, *dev; int err, len; struct rtattr * data; + int group; if (strcmp(argv[0], "peer") != 0) { usage(); @@ -42,7 +43,7 @@ static int veth_parse_opt(struct link_util *lu, int argc, char **argv, hdr->nlmsg_len += sizeof(struct ifinfomsg); err = iplink_parse(argc - 1, argv + 1, (struct iplink_req *)hdr, - &name, &type, &link, &dev); + &name, &type, &link, &dev, &group); if (err < 0) return err; diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c index 9ef5c09d..7827f91a 100644 --- a/ip/xfrm_policy.c +++ b/ip/xfrm_policy.c @@ -77,7 +77,7 @@ static void usage(void) //fprintf(stderr, "PRIORITY - priority value(default=0)\n"); fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); - fprintf(stderr, "FLAG := [ localok ]\n"); + fprintf(stderr, "FLAG := [ localok | icmp ]\n"); fprintf(stderr, "LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]\n"); fprintf(stderr, "LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |\n"); @@ -156,6 +156,8 @@ static int xfrm_policy_flag_parse(__u8 *flags, int *argcp, char ***argvp) while (1) { if (strcmp(*argv, "localok") == 0) *flags |= XFRM_POLICY_LOCALOK; + else if (strcmp(*argv, "icmp") == 0) + *flags |= XFRM_POLICY_ICMP; else { PREV_ARG(); /* back track */ break; diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c index 94acd66b..8ac3437a 100644 --- a/ip/xfrm_state.c +++ b/ip/xfrm_state.c @@ -84,7 +84,7 @@ static void usage(void) //fprintf(stderr, "REQID - number(default=0)\n"); fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); - fprintf(stderr, "FLAG := [ noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec ]\n"); + fprintf(stderr, "FLAG := [ noecn | decap-dscp | nopmtudisc | wildrecv | icmp | af-unspec | align4 ]\n"); fprintf(stderr, "ENCAP := ENCAP-TYPE SPORT DPORT OADDR\n"); fprintf(stderr, "ENCAP-TYPE := espinudp | espinudp-nonike\n"); @@ -216,6 +216,8 @@ static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) *flags |= XFRM_STATE_ICMP; else if (strcmp(*argv, "af-unspec") == 0) *flags |= XFRM_STATE_AF_UNSPEC; + else if (strcmp(*argv, "align4") == 0) + *flags |= XFRM_STATE_ALIGN4; else { PREV_ARG(); /* back track */ break; diff --git a/lib/ll_proto.c b/lib/ll_proto.c index cdd1602e..2277cda3 100644 --- a/lib/ll_proto.c +++ b/lib/ll_proto.c @@ -36,19 +36,13 @@ static struct { } llproto_names[] = { __PF(LOOP,loop) __PF(PUP,pup) -#ifdef ETH_P_PUPAT __PF(PUPAT,pupat) -#endif __PF(IP,ip) __PF(X25,x25) __PF(ARP,arp) __PF(BPQ,bpq) -#ifdef ETH_P_IEEEPUP __PF(IEEEPUP,ieeepup) -#endif -#ifdef ETH_P_IEEEPUPAT __PF(IEEEPUPAT,ieeepupat) -#endif __PF(DEC,dec) __PF(DNA_DL,dna_dl) __PF(DNA_RC,dna_rc) @@ -62,19 +56,10 @@ __PF(ATALK,atalk) __PF(AARP,aarp) __PF(IPX,ipx) __PF(IPV6,ipv6) -#ifdef ETH_P_PPP_DISC __PF(PPP_DISC,ppp_disc) -#endif -#ifdef ETH_P_PPP_SES __PF(PPP_SES,ppp_ses) -#endif -#ifdef ETH_P_ATMMPOA __PF(ATMMPOA,atmmpoa) -#endif -#ifdef ETH_P_ATMFATE __PF(ATMFATE,atmfate) -#endif - __PF(802_3,802_3) __PF(AX25,ax25) __PF(ALL,all) @@ -90,9 +75,7 @@ __PF(TR_802_2,tr_802_2) __PF(MOBITEX,mobitex) __PF(CONTROL,control) __PF(IRDA,irda) -#ifdef ETH_P_ECONET __PF(ECONET,econet) -#endif __PF(TIPC,tipc) __PF(AOE,aoe) diff --git a/man/man8/ip.8 b/man/man8/ip.8 index 23ba4adf..c5248ef4 100644 --- a/man/man8/ip.8 +++ b/man/man8/ip.8 @@ -55,8 +55,10 @@ ip \- show / manipulate routing, devices, policy routing and tunnels .RI "[ " ARGS " ]" .ti -8 -.BI "ip link set " DEVICE -.RB "{ " up " | " down " | " arp " { " on " | " off " } |" +.BR "ip link set " { +.IR DEVICE " | " +.BI "group " GROUP +.RB "} { " up " | " down " | " arp " { " on " | " off " } |" .br .BR promisc " { " on " | " off " } |" .br @@ -95,7 +97,12 @@ ip \- show / manipulate routing, devices, policy routing and tunnels .B qos .IR VLAN-QOS " ] ] [" .B rate -.IR TXRATE " ]" +.IR TXRATE " ] |" +.br +.B master +.IR DEVICE +.br +.B nomaster .ti -8 .B ip link show @@ -929,6 +936,13 @@ specifies network device to operate on. When configuring SR-IOV Virtual Fuction (VF) devices, this keyword should specify the associated Physical Function (PF) device. +.TP +.BI group " GROUP " +.I GROUP +has a dual role: If both group and dev are present, then move the device to the +specified group. If only a group is specified, then the command operates on +all devices in that group. + .TP .BR up " and " down change the state of the device to @@ -995,6 +1009,12 @@ move the device to the network namespace associated with the process .BI alias " NAME" give the device a symbolic name for easy reference. +.TP +.BI group " GROUP" +specify the group the device belongs to. +The available groups are listed in file +.BR "/etc/iproute2/group" . + .TP .BI vf " NUM" specify a Virtual Function device to be configured. The associated PF device @@ -1040,6 +1060,14 @@ Setting this parameter to 0 disables rate limiting. The parameter must be specified. .in -8 +.TP +.BI master " DEVICE" +set master device of the device (enslave device). + +.TP +.BI nomaster +unset master device of the device (release device). + .PP .B Warning: If multiple parameter changes are requested, diff --git a/misc/rtacct.c b/misc/rtacct.c index a247dfd2..ab8fdbb8 100644 --- a/misc/rtacct.c +++ b/misc/rtacct.c @@ -535,7 +535,7 @@ int main(int argc, char *argv[]) } if (getenv("RTACCT_HISTORY")) - snprintf(hist_name, sizeof(hist_name), getenv("RTACCT_HISTORY")); + snprintf(hist_name, sizeof(hist_name), "%s", getenv("RTACCT_HISTORY")); else sprintf(hist_name, "/tmp/.rtacct.u%d", getuid()); @@ -563,7 +563,10 @@ int main(int argc, char *argv[]) exit(-1); } if (stb.st_size != sizeof(*hist_db)) - write(fd, kern_db, sizeof(*hist_db)); + if (write(fd, kern_db, sizeof(*hist_db)) < 0) { + perror("rtacct: write history file"); + exit(-1); + } hist_db = mmap(NULL, sizeof(*hist_db), PROT_READ|PROT_WRITE, diff --git a/tc/Makefile b/tc/Makefile index 101cc835..961e5ce7 100644 --- a/tc/Makefile +++ b/tc/Makefile @@ -15,6 +15,8 @@ TCMODULES += q_cbq.o TCMODULES += q_rr.o TCMODULES += q_multiq.o TCMODULES += q_netem.o +TCMODULES += q_choke.o +TCMODULES += q_sfb.o TCMODULES += f_rsvp.o TCMODULES += f_u32.o TCMODULES += f_route.o @@ -43,6 +45,7 @@ TCMODULES += em_nbyte.o TCMODULES += em_cmp.o TCMODULES += em_u32.o TCMODULES += em_meta.o +TCMODULES += q_mqprio.o TCSO := ifeq ($(TC_CONFIG_ATM),y) diff --git a/tc/f_u32.c b/tc/f_u32.c index d9d40914..c77e9cc8 100644 --- a/tc/f_u32.c +++ b/tc/f_u32.c @@ -432,7 +432,8 @@ static int parse_ip6_class(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) key = htonl(key); mask = htonl(mask); - if (res = pack_key(sel, key, mask, off, offmask) < 0) + res = pack_key(sel, key, mask, off, offmask); + if (res < 0) return -1; *argc_p = argc; diff --git a/tc/q_choke.c b/tc/q_choke.c new file mode 100644 index 00000000..6e54cb71 --- /dev/null +++ b/tc/q_choke.c @@ -0,0 +1,215 @@ +/* + * q_choke.c CHOKE. + * + * 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: Stephen Hemminger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tc_util.h" + +#include "tc_red.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... choke limit PACKETS bandwidth KBPS [ecn]\n"); + fprintf(stderr, " [ min PACKETS ] [ max PACKETS ] [ burst PACKETS ]\n"); +} + +static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct tc_red_qopt opt; + unsigned burst = 0; + unsigned avpkt = 1000; + double probability = 0.02; + unsigned rate = 0; + int ecn_ok = 0; + int wlog; + __u8 sbuf[256]; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_unsigned(&opt.limit, *argv, 0)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + } else if (strcmp(*argv, "ecn") == 0) { + ecn_ok = 1; + } else if (strcmp(*argv, "min") == 0) { + NEXT_ARG(); + if (get_unsigned(&opt.qth_min, *argv, 0)) { + fprintf(stderr, "Illegal \"min\"\n"); + return -1; + } + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_unsigned(&opt.qth_max, *argv, 0)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + } else if (strcmp(*argv, "burst") == 0) { + NEXT_ARG(); + if (get_unsigned(&burst, *argv, 0)) { + fprintf(stderr, "Illegal \"burst\"\n"); + return -1; + } + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + fprintf(stderr, "Illegal \"avpkt\"\n"); + return -1; + } + } else if (strcmp(*argv, "probability") == 0) { + NEXT_ARG(); + if (sscanf(*argv, "%lg", &probability) != 1) { + fprintf(stderr, "Illegal \"probability\"\n"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!rate || !opt.limit) { + fprintf(stderr, "Required parameter (bandwidth, limit) is missing\n"); + return -1; + } + + /* Compute default min/max thresholds based on + Sally Floyd's recommendations: + http://www.icir.org/floyd/REDparameters.txt + */ + if (!opt.qth_max) + opt.qth_max = opt.limit / 4; + if (!opt.qth_min) + opt.qth_min = opt.qth_max / 3; + if (!burst) + burst = (2 * opt.qth_min + opt.qth_max) / 3; + + if (opt.qth_max > opt.limit) { + fprintf(stderr, "\"max\" is larger than \"limit\"\n"); + return -1; + } + + if (opt.qth_min > opt.qth_min) { + fprintf(stderr, "\"min\" is not smaller than \"max\"\n"); + return -1; + } + + wlog = tc_red_eval_ewma(opt.qth_min*avpkt, burst, avpkt); + if (wlog < 0) { + fprintf(stderr, "CHOKE: failed to calculate EWMA constant.\n"); + return -1; + } + if (wlog >= 10) + fprintf(stderr, "CHOKE: WARNING. Burst %d seems to be to large.\n", burst); + opt.Wlog = wlog; + + wlog = tc_red_eval_P(opt.qth_min*avpkt, opt.qth_max*avpkt, probability); + if (wlog < 0) { + fprintf(stderr, "CHOKE: failed to calculate probability.\n"); + return -1; + } + opt.Plog = wlog; + + wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf); + if (wlog < 0) { + fprintf(stderr, "CHOKE: failed to calculate idle damping table.\n"); + return -1; + } + opt.Scell_log = wlog; + if (ecn_ok) + opt.flags |= TC_RED_ECN; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_CHOKE_PARMS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_CHOKE_STAB, sbuf, 256); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int choke_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_CHOKE_STAB+1]; + const struct tc_red_qopt *qopt; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_CHOKE_STAB, opt); + + if (tb[TCA_CHOKE_PARMS] == NULL) + return -1; + qopt = RTA_DATA(tb[TCA_CHOKE_PARMS]); + if (RTA_PAYLOAD(tb[TCA_CHOKE_PARMS]) < sizeof(*qopt)) + return -1; + + fprintf(f, "limit %up min %up max %up ", + qopt->limit, qopt->qth_min, qopt->qth_max); + + if (qopt->flags & TC_RED_ECN) + fprintf(f, "ecn "); + + if (show_details) { + fprintf(f, "ewma %u Plog %u Scell_log %u", + qopt->Wlog, qopt->Plog, qopt->Scell_log); + } + return 0; +} + +static int choke_print_xstats(struct qdisc_util *qu, FILE *f, + struct rtattr *xstats) +{ + struct tc_choke_xstats *st; + + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " marked %u early %u pdrop %u other %u matched %u", + st->marked, st->early, st->pdrop, st->other, st->matched); + return 0; + +} + +struct qdisc_util choke_qdisc_util = { + .id = "choke", + .parse_qopt = choke_parse_opt, + .print_qopt = choke_print_opt, + .print_xstats = choke_print_xstats, +}; diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c new file mode 100644 index 00000000..c589b4ca --- /dev/null +++ b/tc/q_mqprio.c @@ -0,0 +1,124 @@ +/* + * q_mqprio.c MQ prio qdisc + * + * 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. + * + * Author: John Fastabend, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... mqprio [num_tc NUMBER] [map P0 P1 ...]\n"); + fprintf(stderr, " [offset txq0 txq1 ...] "); + fprintf(stderr, "[count cnt0,cnt1 ...] [hw 1|0]\n"); +} + +static int mqprio_parse_opt(struct qdisc_util *qu, int argc, + char **argv, struct nlmsghdr *n) +{ + int idx; + struct tc_mqprio_qopt opt = { + 8, + {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 1, 3, 3, 3, 3}, + 1, + }; + + while (argc > 0) { + idx = 0; + if (strcmp(*argv, "num_tc") == 0) { + NEXT_ARG(); + if (get_u8(&opt.num_tc, *argv, 10)) { + fprintf(stderr, "Illegal \"num_tc\"\n"); + return -1; + } + } else if (strcmp(*argv, "map") == 0) { + while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { + NEXT_ARG(); + if (get_u8(&opt.prio_tc_map[idx], *argv, 10)) { + PREV_ARG(); + break; + } + idx++; + } + for ( ; idx < TC_QOPT_MAX_QUEUE; idx++) + opt.prio_tc_map[idx] = 0; + } else if (strcmp(*argv, "offset") == 0) { + while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { + NEXT_ARG(); + if (get_u16(&opt.offset[idx], *argv, 10)) { + PREV_ARG(); + break; + } + idx++; + } + } else if (strcmp(*argv, "count") == 0) { + while (idx < TC_QOPT_MAX_QUEUE && NEXT_ARG_OK()) { + NEXT_ARG(); + if (get_u16(&opt.count[idx], *argv, 10)) { + PREV_ARG(); + break; + } + idx++; + } + } else if (strcmp(*argv, "hw") == 0) { + NEXT_ARG(); + if (get_u8(&opt.hw, *argv, 10)) { + fprintf(stderr, "Illegal \"hw\"\n"); + return -1; + } + idx++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "Unknown argument\n"); + return -1; + } + argc--; argv++; + } + + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +int mqprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + int i; + struct tc_mqprio_qopt *qopt; + + if (opt == NULL) + return 0; + + qopt = RTA_DATA(opt); + + fprintf(f, " tc %u map ", qopt->num_tc); + for (i = 0; i <= TC_PRIO_MAX; i++) + fprintf(f, "%d ", qopt->prio_tc_map[i]); + fprintf(f, "\n queues:"); + for (i = 0; i < qopt->num_tc; i++) + fprintf(f, "(%i:%i) ", qopt->offset[i], + qopt->offset[i] + qopt->count[i] - 1); + return 0; +} + +struct qdisc_util mqprio_qdisc_util = { + .id = "mqprio", + .parse_qopt = mqprio_parse_opt, + .print_qopt = mqprio_print_opt, +}; diff --git a/tc/q_netem.c b/tc/q_netem.c index 6aaaded9..01639a87 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Authors: Stephen Hemminger + * Authors: Stephen Hemminger * */ diff --git a/tc/q_sfb.c b/tc/q_sfb.c new file mode 100644 index 00000000..f11c33aa --- /dev/null +++ b/tc/q_sfb.c @@ -0,0 +1,200 @@ +/* + * q_sfb.c Stochastic Fair Blue. + * + * 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: Juliusz Chroboczek + * + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, + "Usage: ... sfb [ rehash SECS ] [ db SECS ]\n" + " [ limit PACKETS ] [ max PACKETS ] [ target PACKETS ]\n" + " [ increment FLOAT ] [ decrement FLOAT ]\n" + " [ penalty_rate PPS ] [ penalty_burst PACKETS ]\n"); +} + +static int get_prob(__u32 *val, const char *arg) +{ + double d; + char *ptr; + + if (!arg || !*arg) + return -1; + d = strtod(arg, &ptr); + if (!ptr || ptr == arg || d < 0.0 || d > 1.0) + return -1; + *val = (__u32)(d * SFB_MAX_PROB + 0.5); + return 0; +} + +static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct tc_sfb_qopt opt; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + opt.rehash_interval = 600*1000; + opt.warmup_time = 60*1000; + opt.penalty_rate = 10; + opt.penalty_burst = 20; + opt.increment = (SFB_MAX_PROB + 1000) / 2000; + opt.decrement = (SFB_MAX_PROB + 10000) / 20000; + + while (argc > 0) { + if (strcmp(*argv, "rehash") == 0) { + NEXT_ARG(); + if (get_u32(&opt.rehash_interval, *argv, 0)) { + fprintf(stderr, "Illegal \"rehash\"\n"); + return -1; + } + } else if (strcmp(*argv, "db") == 0) { + NEXT_ARG(); + if (get_u32(&opt.warmup_time, *argv, 0)) { + fprintf(stderr, "Illegal \"db\"\n"); + return -1; + } + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_u32(&opt.limit, *argv, 0)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_u32(&opt.max, *argv, 0)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + } else if (strcmp(*argv, "target") == 0) { + NEXT_ARG(); + if (get_u32(&opt.bin_size, *argv, 0)) { + fprintf(stderr, "Illegal \"target\"\n"); + return -1; + } + } else if (strcmp(*argv, "increment") == 0) { + NEXT_ARG(); + if (get_prob(&opt.increment, *argv)) { + fprintf(stderr, "Illegal \"increment\"\n"); + return -1; + } + } else if (strcmp(*argv, "decrement") == 0) { + NEXT_ARG(); + if (get_prob(&opt.decrement, *argv)) { + fprintf(stderr, "Illegal \"decrement\"\n"); + return -1; + } + } else if (strcmp(*argv, "penalty_rate") == 0) { + NEXT_ARG(); + if (get_u32(&opt.penalty_rate, *argv, 0)) { + fprintf(stderr, "Illegal \"penalty_rate\"\n"); + return -1; + } + } else if (strcmp(*argv, "penalty_burst") == 0) { + NEXT_ARG(); + if (get_u32(&opt.penalty_burst, *argv, 0)) { + fprintf(stderr, "Illegal \"penalty_burst\"\n"); + return -1; + } + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (opt.max == 0) { + if (opt.bin_size >= 1) + opt.max = (opt.bin_size * 5 + 1) / 4; + else + opt.max = 25; + } + if (opt.bin_size == 0) + opt.bin_size = (opt.max * 4 + 3) / 5; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_SFB_PARMS, &opt, sizeof(opt)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int sfb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[__TCA_SFB_MAX]; + struct tc_sfb_qopt *qopt; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_SFB_MAX, opt); + if (tb[TCA_SFB_PARMS] == NULL) + return -1; + qopt = RTA_DATA(tb[TCA_SFB_PARMS]); + if (RTA_PAYLOAD(tb[TCA_SFB_PARMS]) < sizeof(*qopt)) + return -1; + + fprintf(f, + "limit %d max %d target %d\n" + " increment %.5f decrement %.5f penalty rate %d burst %d " + "(%ums %ums)", + qopt->limit, qopt->max, qopt->bin_size, + (double)qopt->increment / SFB_MAX_PROB, + (double)qopt->decrement / SFB_MAX_PROB, + qopt->penalty_rate, qopt->penalty_burst, + qopt->rehash_interval, qopt->warmup_time); + + return 0; +} + +static int sfb_print_xstats(struct qdisc_util *qu, FILE *f, + struct rtattr *xstats) +{ + struct tc_sfb_xstats *st; + + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, + " earlydrop %u penaltydrop %u bucketdrop %u queuedrop %u childdrop %u marked %u\n" + " maxqlen %u maxprob %.5f avgprob %.5f ", + st->earlydrop, st->penaltydrop, st->bucketdrop, st->queuedrop, st->childdrop, + st->marked, + st->maxqlen, (double)st->maxprob / SFB_MAX_PROB, + (double)st->avgprob / SFB_MAX_PROB); + + return 0; +} + +struct qdisc_util sfb_qdisc_util = { + .id = "sfb", + .parse_qopt = sfb_parse_opt, + .print_qopt = sfb_print_opt, + .print_xstats = sfb_print_xstats, +}; diff --git a/tc/q_sfq.c b/tc/q_sfq.c index 71a3c9ac..98ec530d 100644 --- a/tc/q_sfq.c +++ b/tc/q_sfq.c @@ -26,6 +26,7 @@ static void explain(void) { fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n"); + fprintf(stderr, " [ divisor NUMBER ]\n"); } static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) @@ -61,6 +62,13 @@ static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl return -1; } ok++; + } else if (strcmp(*argv, "divisor") == 0) { + NEXT_ARG(); + if (get_u32(&opt.divisor, *argv, 0)) { + fprintf(stderr, "Illegal \"divisor\"\n"); + return -1; + } + ok++; } else if (strcmp(*argv, "help") == 0) { explain(); return -1; @@ -93,6 +101,7 @@ static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) if (show_details) { fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor); } + fprintf(f, "divisor %u ", qopt->divisor); if (qopt->perturb_period) fprintf(f, "perturb %dsec ", qopt->perturb_period); return 0;