From 4f7d3057911ebc5df78113896cb02e1b4ffbfabe Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Wed, 22 Feb 2006 21:29:08 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r1650, which included commits to RCS files with non-trunk default branches. --- COPYING | 340 + ChangeLog | 332 + Config | 1 + Makefile | 68 + Makefile.kernel | 67 + README | 34 + README.decnet | 41 + README.distribution | 95 + README.iproute2+tc | 119 + README.lnstat | 81 + RELNOTES | 168 + configure | 27 + doc/Makefile | 55 + doc/Plan | 16 + doc/SNAPSHOT.tex | 1 + doc/actions/gact-usage | 79 + doc/actions/mirred-usage | 71 + doc/api-ip6-flowlabels.tex | 429 + doc/arpd.sgml | 130 + doc/do-psnup | 16 + doc/ip-cref.tex | 3316 +++++ doc/ip-tunnels.tex | 469 + doc/nstat.sgml | 110 + doc/preamble.tex | 26 + doc/rtstat.sgml | 52 + doc/ss.sgml | 525 + etc/iproute2/rt_dsfield | 15 + etc/iproute2/rt_dsfield.rt_config | 13 + etc/iproute2/rt_protos | 26 + etc/iproute2/rt_protos.rt_config | 25 + etc/iproute2/rt_realms | 13 + etc/iproute2/rt_realms.rt_config | 13 + etc/iproute2/rt_scopes | 12 + etc/iproute2/rt_scopes.rt_config | 11 + etc/iproute2/rt_tables | 11 + etc/iproute2/rt_tables.rt_config | 11 + examples/SYN-DoS.rate.limit | 49 + examples/cbqinit.eth1 | 76 + examples/dhcp-client-script | 446 + examples/diffserv/Edge1 | 68 + examples/diffserv/Edge2 | 87 + examples/diffserv/Edge31-ca-u32 | 170 + examples/diffserv/Edge31-cb-chains | 132 + examples/diffserv/Edge32-ca-u32 | 198 + examples/diffserv/Edge32-cb-chains | 144 + examples/diffserv/Edge32-cb-u32 | 145 + examples/diffserv/README | 98 + examples/diffserv/afcbq | 105 + examples/diffserv/ef-prio | 25 + examples/diffserv/efcbq | 31 + examples/diffserv/regression-testing | 125 + include/SNAPSHOT.h | 1 + include/ip6tables.h | 141 + include/iptables.h | 155 + include/iptables_common.h | 37 + include/libiptc/ipt_kernel_headers.h | 27 + include/libiptc/libip6tc.h | 154 + include/libiptc/libiptc.h | 166 + include/libnetlink.h | 57 + include/linux/gen_stats.h | 67 + include/linux/netfilter_ipv4/ip_tables.h | 347 + include/linux/netlink.h | 101 + include/linux/pkt_cls.h | 425 + include/linux/pkt_sched.h | 454 + include/linux/rtnetlink.h | 749 ++ include/linux/tc_act/tc_gact.h | 34 + include/linux/tc_act/tc_ipt.h | 21 + include/linux/tc_act/tc_mirred.h | 28 + include/linux/tc_act/tc_pedit.h | 36 + include/linux/tcp.h | 194 + include/linux/tcp_diag.h | 127 + include/linux/xfrm.h | 258 + include/ll_map.h | 13 + include/rt_names.h | 30 + include/rtm_map.h | 10 + include/utils.h | 126 + ip/Makefile | 24 + ip/ifcfg | 145 + ip/ip | Bin 0 -> 153676 bytes ip/ip.c | 171 + ip/ip.o | Bin 0 -> 6928 bytes ip/ip_common.h | 29 + ip/ipaddress.c | 904 ++ ip/ipaddress.o | Bin 0 -> 28616 bytes ip/iplink.c | 429 + ip/iplink.o | Bin 0 -> 11376 bytes ip/ipmaddr.c | 343 + ip/ipmaddr.o | Bin 0 -> 9832 bytes ip/ipmonitor.c | 165 + ip/ipmonitor.o | Bin 0 -> 5208 bytes ip/ipmroute.c | 205 + ip/ipmroute.o | Bin 0 -> 6872 bytes ip/ipneigh.c | 475 + ip/ipneigh.o | Bin 0 -> 15680 bytes ip/ipprefix.c | 106 + ip/ipprefix.o | Bin 0 -> 3272 bytes ip/iproute.c | 1416 +++ ip/iproute.c.initvar | 1413 +++ ip/iproute.o | Bin 0 -> 44480 bytes ip/iprule.c | 383 + ip/iprule.o | Bin 0 -> 11912 bytes ip/iptunnel.c | 585 + ip/iptunnel.o | Bin 0 -> 17440 bytes ip/ipxfrm.c | 1051 ++ ip/ipxfrm.o | Bin 0 -> 26728 bytes ip/routef | 3 + ip/routel | 60 + ip/rtm_map.c | 116 + ip/rtm_map.o | Bin 0 -> 4088 bytes ip/rtmon | Bin 0 -> 30290 bytes ip/rtmon.c | 178 + ip/rtmon.o | Bin 0 -> 5720 bytes ip/rtpr | 4 + ip/xfrm.h | 119 + ip/xfrm_policy.c | 736 ++ ip/xfrm_policy.o | Bin 0 -> 21080 bytes ip/xfrm_state.c | 762 ++ ip/xfrm_state.o | Bin 0 -> 21336 bytes lib/Makefile | 18 + lib/dnet_ntop.c | 98 + lib/dnet_ntop.o | Bin 0 -> 1920 bytes lib/dnet_pton.c | 71 + lib/dnet_pton.o | Bin 0 -> 1736 bytes lib/inet_proto.c | 70 + lib/inet_proto.o | Bin 0 -> 2616 bytes lib/ipx_ntop.c | 71 + lib/ipx_ntop.o | Bin 0 -> 1696 bytes lib/ipx_pton.c | 107 + lib/ipx_pton.o | Bin 0 -> 2008 bytes lib/libnetlink.a | Bin 0 -> 16776 bytes lib/libnetlink.c | 580 + lib/libnetlink.o | Bin 0 -> 11928 bytes lib/libutil.a | Bin 0 -> 56136 bytes lib/ll_addr.c | 93 + lib/ll_addr.o | Bin 0 -> 2624 bytes lib/ll_map.c | 170 + lib/ll_map.o | Bin 0 -> 4216 bytes lib/ll_proto.c | 129 + lib/ll_proto.o | Bin 0 -> 4120 bytes lib/ll_types.c | 134 + lib/ll_types.o | Bin 0 -> 4592 bytes lib/rt_names.c | 397 + lib/rt_names.o | Bin 0 -> 21104 bytes lib/utils.c | 558 + lib/utils.o | Bin 0 -> 12208 bytes man/man3/libnetlink.3 | 197 + man/man8/ip.8 | 1810 +++ man/man8/tc-cbq-details.8 | 425 + man/man8/tc-cbq.8 | 353 + man/man8/tc-htb.8 | 150 + man/man8/tc-pbfifo.8 | 72 + man/man8/tc-pfifo_fast.8 | 59 + man/man8/tc-prio.8 | 187 + man/man8/tc-red.8 | 131 + man/man8/tc-sfq.8 | 107 + man/man8/tc-tbf.8 | 138 + man/man8/tc.8 | 348 + misc/Makefile | 35 + misc/arpd | Bin 0 -> 41062 bytes misc/arpd.c | 846 ++ misc/ifstat | Bin 0 -> 33086 bytes misc/ifstat.c | 766 ++ misc/lnstat | Bin 0 -> 19999 bytes misc/lnstat.c | 336 + misc/lnstat.h | 43 + misc/lnstat.o | Bin 0 -> 11104 bytes misc/lnstat_util.c | 324 + misc/lnstat_util.o | Bin 0 -> 6688 bytes misc/netbug | 53 + misc/nstat | Bin 0 -> 22421 bytes misc/nstat.c | 620 + misc/rtacct | Bin 0 -> 37528 bytes misc/rtacct.c | 625 + misc/ss | Bin 0 -> 75189 bytes misc/ss.c | 2803 +++++ misc/ss.o | Bin 0 -> 68184 bytes misc/ssfilter.c | 1581 +++ misc/ssfilter.h | 21 + misc/ssfilter.o | Bin 0 -> 10032 bytes misc/ssfilter.y | 275 + netem/Makefile | 27 + netem/README.distribution | 97 + netem/experimental.dat | 13448 +++++++++++++++++++++ netem/experimental.dist | 513 + netem/maketable | Bin 0 -> 10667 bytes netem/maketable.c | 232 + netem/normal | Bin 0 -> 8450 bytes netem/normal.c | 56 + netem/normal.dist | 513 + netem/pareto | Bin 0 -> 7772 bytes netem/pareto.c | 41 + netem/pareto.dist | 513 + netem/paretonormal | Bin 0 -> 8891 bytes netem/paretonormal.c | 90 + netem/paretonormal.dist | 513 + tc/Makefile | 74 + tc/README.last | 47 + tc/f_fw.c | 140 + tc/f_fw.o | Bin 0 -> 5408 bytes tc/f_route.c | 169 + tc/f_route.o | Bin 0 -> 6952 bytes tc/f_rsvp.c | 404 + tc/f_rsvp.o | Bin 0 -> 12128 bytes tc/f_tcindex.c | 185 + tc/f_tcindex.o | Bin 0 -> 5432 bytes tc/f_u32.c | 1071 ++ tc/f_u32.o | Bin 0 -> 23608 bytes tc/libtc.a | Bin 0 -> 11328 bytes tc/m_action.c | 608 + tc/m_action.o | Bin 0 -> 14408 bytes tc/m_estimator.c | 64 + tc/m_estimator.o | Bin 0 -> 3552 bytes tc/m_gact.c | 244 + tc/m_gact.o | Bin 0 -> 6128 bytes tc/m_ipt.c | 599 + tc/m_ipt.o | Bin 0 -> 13552 bytes tc/m_mirred.c | 292 + tc/m_mirred.o | Bin 0 -> 9336 bytes tc/m_pedit.c | 604 + tc/m_pedit.h | 62 + tc/m_pedit.o | Bin 0 -> 13000 bytes tc/m_police.c | 359 + tc/m_police.o | Bin 0 -> 12456 bytes tc/p_icmp.c | 61 + tc/p_icmp.o | Bin 0 -> 1416 bytes tc/p_ip.c | 159 + tc/p_ip.o | Bin 0 -> 3576 bytes tc/p_tcp.c | 38 + tc/p_tcp.o | Bin 0 -> 1408 bytes tc/p_udp.c | 38 + tc/p_udp.o | Bin 0 -> 1408 bytes tc/q_atm.c | 260 + tc/q_cbq.c | 553 + tc/q_cbq.o | Bin 0 -> 16232 bytes tc/q_dsmark.c | 179 + tc/q_dsmark.o | Bin 0 -> 5312 bytes tc/q_fifo.c | 98 + tc/q_fifo.o | Bin 0 -> 3608 bytes tc/q_gred.c | 312 + tc/q_gred.o | Bin 0 -> 9664 bytes tc/q_hfsc.c | 406 + tc/q_hfsc.o | Bin 0 -> 10408 bytes tc/q_htb.c | 330 + tc/q_htb.o | Bin 0 -> 11496 bytes tc/q_ingress.c | 69 + tc/q_ingress.o | Bin 0 -> 2472 bytes tc/q_netem.c | 293 + tc/q_netem.so | Bin 0 -> 12731 bytes tc/q_prio.c | 119 + tc/q_prio.o | Bin 0 -> 4000 bytes tc/q_red.c | 219 + tc/q_red.o | Bin 0 -> 7304 bytes tc/q_sfq.c | 107 + tc/q_sfq.o | Bin 0 -> 4056 bytes tc/q_tbf.c | 264 + tc/q_tbf.o | Bin 0 -> 9552 bytes tc/tc | Bin 0 -> 189642 bytes tc/tc.c | 347 + tc/tc.o | Bin 0 -> 10544 bytes tc/tc_cbq.c | 57 + tc/tc_cbq.h | 9 + tc/tc_cbq.o | Bin 0 -> 2728 bytes tc/tc_class.c | 319 + tc/tc_class.o | Bin 0 -> 11568 bytes tc/tc_common.h | 11 + tc/tc_core.c | 89 + tc/tc_core.h | 19 + tc/tc_core.o | Bin 0 -> 3080 bytes tc/tc_estimator.c | 44 + tc/tc_estimator.o | Bin 0 -> 1992 bytes tc/tc_filter.c | 371 + tc/tc_filter.o | Bin 0 -> 13720 bytes tc/tc_qdisc.c | 319 + tc/tc_qdisc.o | Bin 0 -> 11424 bytes tc/tc_red.c | 97 + tc/tc_red.h | 8 + tc/tc_red.o | Bin 0 -> 2976 bytes tc/tc_util.c | 519 + tc/tc_util.h | 84 + tc/tc_util.o | Bin 0 -> 16896 bytes testsuite/Makefile | 33 + testsuite/configs/all-2.4 | 848 ++ testsuite/configs/all-no-act | 1499 +++ testsuite/configs/all-police-act | 1504 +++ testsuite/tests/policer | 13 + testsuite/tests/std-cbq | 10 + 286 files changed, 67768 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Config create mode 100644 Makefile create mode 100644 Makefile.kernel create mode 100644 README create mode 100644 README.decnet create mode 100644 README.distribution create mode 100644 README.iproute2+tc create mode 100644 README.lnstat create mode 100644 RELNOTES create mode 100755 configure create mode 100644 doc/Makefile create mode 100644 doc/Plan create mode 100644 doc/SNAPSHOT.tex create mode 100644 doc/actions/gact-usage create mode 100644 doc/actions/mirred-usage create mode 100644 doc/api-ip6-flowlabels.tex create mode 100644 doc/arpd.sgml create mode 100755 doc/do-psnup create mode 100644 doc/ip-cref.tex create mode 100644 doc/ip-tunnels.tex create mode 100644 doc/nstat.sgml create mode 100644 doc/preamble.tex create mode 100644 doc/rtstat.sgml create mode 100644 doc/ss.sgml create mode 100644 etc/iproute2/rt_dsfield create mode 100644 etc/iproute2/rt_dsfield.rt_config create mode 100644 etc/iproute2/rt_protos create mode 100644 etc/iproute2/rt_protos.rt_config create mode 100644 etc/iproute2/rt_realms create mode 100644 etc/iproute2/rt_realms.rt_config create mode 100644 etc/iproute2/rt_scopes create mode 100644 etc/iproute2/rt_scopes.rt_config create mode 100644 etc/iproute2/rt_tables create mode 100644 etc/iproute2/rt_tables.rt_config create mode 100644 examples/SYN-DoS.rate.limit create mode 100755 examples/cbqinit.eth1 create mode 100755 examples/dhcp-client-script create mode 100644 examples/diffserv/Edge1 create mode 100644 examples/diffserv/Edge2 create mode 100644 examples/diffserv/Edge31-ca-u32 create mode 100644 examples/diffserv/Edge31-cb-chains create mode 100644 examples/diffserv/Edge32-ca-u32 create mode 100644 examples/diffserv/Edge32-cb-chains create mode 100644 examples/diffserv/Edge32-cb-u32 create mode 100644 examples/diffserv/README create mode 100644 examples/diffserv/afcbq create mode 100644 examples/diffserv/ef-prio create mode 100644 examples/diffserv/efcbq create mode 100644 examples/diffserv/regression-testing create mode 100644 include/SNAPSHOT.h create mode 100644 include/ip6tables.h create mode 100644 include/iptables.h create mode 100644 include/iptables_common.h create mode 100644 include/libiptc/ipt_kernel_headers.h create mode 100644 include/libiptc/libip6tc.h create mode 100644 include/libiptc/libiptc.h create mode 100644 include/libnetlink.h create mode 100644 include/linux/gen_stats.h create mode 100644 include/linux/netfilter_ipv4/ip_tables.h create mode 100644 include/linux/netlink.h create mode 100644 include/linux/pkt_cls.h create mode 100644 include/linux/pkt_sched.h create mode 100644 include/linux/rtnetlink.h create mode 100644 include/linux/tc_act/tc_gact.h create mode 100644 include/linux/tc_act/tc_ipt.h create mode 100644 include/linux/tc_act/tc_mirred.h create mode 100644 include/linux/tc_act/tc_pedit.h create mode 100644 include/linux/tcp.h create mode 100644 include/linux/tcp_diag.h create mode 100644 include/linux/xfrm.h create mode 100644 include/ll_map.h create mode 100644 include/rt_names.h create mode 100644 include/rtm_map.h create mode 100644 include/utils.h create mode 100644 ip/Makefile create mode 100755 ip/ifcfg create mode 100755 ip/ip create mode 100644 ip/ip.c create mode 100644 ip/ip.o create mode 100644 ip/ip_common.h create mode 100644 ip/ipaddress.c create mode 100644 ip/ipaddress.o create mode 100644 ip/iplink.c create mode 100644 ip/iplink.o create mode 100644 ip/ipmaddr.c create mode 100644 ip/ipmaddr.o create mode 100644 ip/ipmonitor.c create mode 100644 ip/ipmonitor.o create mode 100644 ip/ipmroute.c create mode 100644 ip/ipmroute.o create mode 100644 ip/ipneigh.c create mode 100644 ip/ipneigh.o create mode 100644 ip/ipprefix.c create mode 100644 ip/ipprefix.o create mode 100644 ip/iproute.c create mode 100644 ip/iproute.c.initvar create mode 100644 ip/iproute.o create mode 100644 ip/iprule.c create mode 100644 ip/iprule.o create mode 100644 ip/iptunnel.c create mode 100644 ip/iptunnel.o create mode 100644 ip/ipxfrm.c create mode 100644 ip/ipxfrm.o create mode 100755 ip/routef create mode 100755 ip/routel create mode 100644 ip/rtm_map.c create mode 100644 ip/rtm_map.o create mode 100755 ip/rtmon create mode 100644 ip/rtmon.c create mode 100644 ip/rtmon.o create mode 100755 ip/rtpr create mode 100644 ip/xfrm.h create mode 100644 ip/xfrm_policy.c create mode 100644 ip/xfrm_policy.o create mode 100644 ip/xfrm_state.c create mode 100644 ip/xfrm_state.o create mode 100644 lib/Makefile create mode 100644 lib/dnet_ntop.c create mode 100644 lib/dnet_ntop.o create mode 100644 lib/dnet_pton.c create mode 100644 lib/dnet_pton.o create mode 100644 lib/inet_proto.c create mode 100644 lib/inet_proto.o create mode 100644 lib/ipx_ntop.c create mode 100644 lib/ipx_ntop.o create mode 100644 lib/ipx_pton.c create mode 100644 lib/ipx_pton.o create mode 100644 lib/libnetlink.a create mode 100644 lib/libnetlink.c create mode 100644 lib/libnetlink.o create mode 100644 lib/libutil.a create mode 100644 lib/ll_addr.c create mode 100644 lib/ll_addr.o create mode 100644 lib/ll_map.c create mode 100644 lib/ll_map.o create mode 100644 lib/ll_proto.c create mode 100644 lib/ll_proto.o create mode 100644 lib/ll_types.c create mode 100644 lib/ll_types.o create mode 100644 lib/rt_names.c create mode 100644 lib/rt_names.o create mode 100644 lib/utils.c create mode 100644 lib/utils.o create mode 100644 man/man3/libnetlink.3 create mode 100644 man/man8/ip.8 create mode 100644 man/man8/tc-cbq-details.8 create mode 100644 man/man8/tc-cbq.8 create mode 100644 man/man8/tc-htb.8 create mode 100644 man/man8/tc-pbfifo.8 create mode 100644 man/man8/tc-pfifo_fast.8 create mode 100644 man/man8/tc-prio.8 create mode 100644 man/man8/tc-red.8 create mode 100644 man/man8/tc-sfq.8 create mode 100644 man/man8/tc-tbf.8 create mode 100644 man/man8/tc.8 create mode 100644 misc/Makefile create mode 100755 misc/arpd create mode 100644 misc/arpd.c create mode 100755 misc/ifstat create mode 100644 misc/ifstat.c create mode 100755 misc/lnstat create mode 100644 misc/lnstat.c create mode 100644 misc/lnstat.h create mode 100644 misc/lnstat.o create mode 100644 misc/lnstat_util.c create mode 100644 misc/lnstat_util.o create mode 100755 misc/netbug create mode 100755 misc/nstat create mode 100644 misc/nstat.c create mode 100755 misc/rtacct create mode 100644 misc/rtacct.c create mode 100755 misc/ss create mode 100644 misc/ss.c create mode 100644 misc/ss.o create mode 100644 misc/ssfilter.c create mode 100644 misc/ssfilter.h create mode 100644 misc/ssfilter.o create mode 100644 misc/ssfilter.y create mode 100644 netem/Makefile create mode 100644 netem/README.distribution create mode 100644 netem/experimental.dat create mode 100644 netem/experimental.dist create mode 100755 netem/maketable create mode 100644 netem/maketable.c create mode 100755 netem/normal create mode 100644 netem/normal.c create mode 100644 netem/normal.dist create mode 100755 netem/pareto create mode 100644 netem/pareto.c create mode 100644 netem/pareto.dist create mode 100755 netem/paretonormal create mode 100644 netem/paretonormal.c create mode 100644 netem/paretonormal.dist create mode 100644 tc/Makefile create mode 100644 tc/README.last create mode 100644 tc/f_fw.c create mode 100644 tc/f_fw.o create mode 100644 tc/f_route.c create mode 100644 tc/f_route.o create mode 100644 tc/f_rsvp.c create mode 100644 tc/f_rsvp.o create mode 100644 tc/f_tcindex.c create mode 100644 tc/f_tcindex.o create mode 100644 tc/f_u32.c create mode 100644 tc/f_u32.o create mode 100644 tc/libtc.a create mode 100644 tc/m_action.c create mode 100644 tc/m_action.o create mode 100644 tc/m_estimator.c create mode 100644 tc/m_estimator.o create mode 100644 tc/m_gact.c create mode 100644 tc/m_gact.o create mode 100644 tc/m_ipt.c create mode 100644 tc/m_ipt.o create mode 100644 tc/m_mirred.c create mode 100644 tc/m_mirred.o create mode 100644 tc/m_pedit.c create mode 100644 tc/m_pedit.h create mode 100644 tc/m_pedit.o create mode 100644 tc/m_police.c create mode 100644 tc/m_police.o create mode 100644 tc/p_icmp.c create mode 100644 tc/p_icmp.o create mode 100644 tc/p_ip.c create mode 100644 tc/p_ip.o create mode 100644 tc/p_tcp.c create mode 100644 tc/p_tcp.o create mode 100644 tc/p_udp.c create mode 100644 tc/p_udp.o create mode 100644 tc/q_atm.c create mode 100644 tc/q_cbq.c create mode 100644 tc/q_cbq.o create mode 100644 tc/q_dsmark.c create mode 100644 tc/q_dsmark.o create mode 100644 tc/q_fifo.c create mode 100644 tc/q_fifo.o create mode 100644 tc/q_gred.c create mode 100644 tc/q_gred.o create mode 100644 tc/q_hfsc.c create mode 100644 tc/q_hfsc.o create mode 100644 tc/q_htb.c create mode 100644 tc/q_htb.o create mode 100644 tc/q_ingress.c create mode 100644 tc/q_ingress.o create mode 100644 tc/q_netem.c create mode 100755 tc/q_netem.so create mode 100644 tc/q_prio.c create mode 100644 tc/q_prio.o create mode 100644 tc/q_red.c create mode 100644 tc/q_red.o create mode 100644 tc/q_sfq.c create mode 100644 tc/q_sfq.o create mode 100644 tc/q_tbf.c create mode 100644 tc/q_tbf.o create mode 100755 tc/tc create mode 100644 tc/tc.c create mode 100644 tc/tc.o create mode 100644 tc/tc_cbq.c create mode 100644 tc/tc_cbq.h create mode 100644 tc/tc_cbq.o create mode 100644 tc/tc_class.c create mode 100644 tc/tc_class.o create mode 100644 tc/tc_common.h create mode 100644 tc/tc_core.c create mode 100644 tc/tc_core.h create mode 100644 tc/tc_core.o create mode 100644 tc/tc_estimator.c create mode 100644 tc/tc_estimator.o create mode 100644 tc/tc_filter.c create mode 100644 tc/tc_filter.o create mode 100644 tc/tc_qdisc.c create mode 100644 tc/tc_qdisc.o create mode 100644 tc/tc_red.c create mode 100644 tc/tc_red.h create mode 100644 tc/tc_red.o create mode 100644 tc/tc_util.c create mode 100644 tc/tc_util.h create mode 100644 tc/tc_util.o create mode 100644 testsuite/Makefile create mode 100644 testsuite/configs/all-2.4 create mode 100644 testsuite/configs/all-no-act create mode 100644 testsuite/configs/all-police-act create mode 100644 testsuite/tests/policer create mode 100644 testsuite/tests/std-cbq diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2b7b643 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..53bd530 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,332 @@ +2005-03-14 Stephen Hemminger + + * cleanup batch mode, allow continuation, comments etc. + * recode reuse of netlink socket + +2005-03-14 Boian Bonev + + * enhancement to batch mode. + it does not exit on error, just report it + tc reuses the already open netlink socket for subsequent command(s) + +2005-03-14 Thomas Graf + + * ip link command + print NO-CARRIER flag if there is no carrier and the link is up. + +2005-03-14 Patrick McHardy + + * bug: Use USER_HZ where necessary + +2005-03-10 Jamal Hadi Salim + + * Fix bug with register_target + +2005-03-10 Stephen Hemminger + + * fix pkt_cls.h to have tc_u32_mark + * update include files to be stripped versions of 2.6.11 + * add documentation about netem distributions [from nistnet] + * turn off nup in document make [from FC3] + * don't build with extra debug info (-g) [from FC3] + +2005-03-10 Nix + + * make man3 directory + +2005-03-10 Pasi + + * add ESP-in-UDP encapsulation + +2005-03-10 Thomas Graf + * [NETEM] Fix off by one + * update local header file copies + * [NEIGH] print number of probes done so far (statistics mode only) + +2005-03-10 Herbert Xu + * Trivial typo in ip help + +2005-02-09 Stephen Hemminger + + * netem distribution data reorganization + +2005-02-09 Roland Dreier + + * ip over infiniband address display + +2005-02-09 Jim Gifford + + * make install fix for ip/ + +2005-02-07 Mads Martin Joergensen + + * Don't mix address families when flushing + +2005-02-07 Stephen Hemminger + + * Validate classid is not too large to cause loss of bits. + +2005-02-07 Jean-Marc Ranger + + * need to call getline() with null for first usage + * don't overwrite const arg + +2005-02-07 Stephen Hemminger + + * Add experimental distribution + +2005-01-18 Yun Mao + + * typo in ss + +2005-01-18 Thomas Graf + + * tc pedit/action cleanups + * add addraw_l + * rtattr_parse cleanups + +2005-01-17 Jamal Hadi Salim + + * typo in m_mirred + * add support for pedit + +2005-01-13 Jim Gifford + + * Fix allocation size error in nomal and paretonormal generation + programs. + +2005-01-12 Masahide Nakamura + + * ipmonitor shows IPv6 prefix list notification + * update to iproute2 xfrm for ipv6 + +2005-01-12 Stephen Hemminger + + * Fix compile warnings when building 64bit system since + u64 is unsigned long, but format is %llu + +2005-01-12 "Catalin(ux aka Dino) BOIE" + + * Add the possibility to use fwmark in u32 filters + +2005-01-12 Andi Kleen + + * Add netlink manual page + +2004-10-20 Stephen Hemminger + + * Add warning about "ip route nat" no longer supported + +2005-01-12 Thomas Graf + + * Tc testsuite + +2005-01-12 Jamal Hadi Salim + + * Add iptables tc support. This meant borrowing headers + from iptables *ugh* + +2004-12-08 Jamal Hadi Salim + + * Add mirror and redirect actions + +2004-10-20 Stephen Hemminger + + * Don't include since then we get dependant on + kernel headers on host machine + * Minor fix for building on old machine without IPPROTO_SCTP + +2004-10-19 Harald Welte + + * Replace rtstat (and ctstat) with new lnstat + +2004-10-19 Mads Martin Joergensen + + * Ip is using the wrong structure in ipaddress.c when showing stats + * Make sure no buffer overflow in nstat + +2004-10-19 Michal + + * fix scaling in print_rates (for bits) + +2004-09-28 Stephen Hemminger + + * fix build problems with arpd and pthread + * add pkt_sched.h + +2004-09-28 Mike Frysinger + + * make man8 directory + * install ifcfg and rtpr scripts + +2004-09-28 Andreas Haumer + + * make install symlink fix. + +2004-09-28 Masahide Nakamura + + * ICMP/ICMPv6's type and code in IPsec selector. + * fixes `ip xfrm`'s algorithm key when using hexadecimal + * support 'ip xfrm' protocol types + * flush message types for XFRM's policy/state + + +2004-09-01 Stephen Hemminger + + * Fix ip command to not crash when interface name is too long. + always use strncpy(.., IFNAMSIZ) + +2004-08-31 Stephen Hemminger + + * Add gact documentation from jamal + * Chang more arguments to rtnetlink API const + * Drop dead queuing disciplines + * Handle qdisc without xstats in core rather than + putting stub's everywhere + * Add requeue to tc_stats and handle new/old ABI issues + +2004-08-30 Stephen Hemminger + + * Make clean and install changes for man pages + * Patch from jamal to support gact + * Add support for loading distributions to netem + + +2004-08-23 Stephen Hemminger + + * Update from jamal for all the parts that got broken in the + last classification patch. + * Hfsc/sc patch from patrick + +2004-08-13 Stephen Hemminger + + * Add jamal's tc extensions for classification + * Get rid of old Patches/ directory for tcp_diag module + * Make get_rate table based. + +2004-08-11 Stephen Hemminger + + * Add xfrm message formatting from + Masahide Nakamura + +2004-08-09 Stephen Hemminger + + * Fix netem scheduler to handle case where psched us != real us + + * Remove configuration for everything that can depend on + extracted kernel headers + * Add kernel headers required to include/linux + +2004-08-04 Stephen Hemminger + + * Get rid of old tcp_diag module, it is part of kernel. + + * Add some kernel include files back (netlink, tcp_diag, pkt_sched) + +2004-07-30 Stephen Hemminger + + * Make ip xfrm stuff config option since it doesn't exist on 2.4 + + * HFSC doesn't exist on older 2.4 kernels so make it configurable + + * HTB API changed and won't build with mismatched version. + Rather than sticking user with a build error, just don't + build it in on mismatch. + + * Change configure script to make sure netem is the correct + version. I changed the structure def. a couple of times before + settling on the final API + +2004-07-16 Stephen Hemminger + + * Add htb mpu support + http://luxik.cdi.cz/~devik/qos/htb/v3/htb_tc_overhead.diff + * Three small xfrm updates + +2004-07-07 Stephen Hemminger + + * Fix if_ether.h to fix arpd build + * Add hfsc scheduler support + * Add ip xfrm support + * Add add jitter (instead of rate) to netem scheduler + +2004-07-02 Stephen Hemminger + + * use compile to test for ATM libraries + * put TC layered scheduler hooks in /usr/lib/tc as shared lib + before it looked in standard search path (/lib;/usr/lib;...) + which seems out of place. + * build netem as shared library (more for testing/example) + * build ATM as shared library since libatm may be on build + machine but not on deployment machine + * fix make install to not install SCCS directories + +2004-07-01 Stephen Hemminger + + * add more link options to ip command (from Mark Smith + * add rate and duplicate arguments to tc command + * add -iec flag for tc printout + * rename delay scheduler to netem + +2004-06-25 Stephen Hemminger + + * Add loss parameter to delay + * Rename delay qdisc to netsim + * Add autoconfiguration by building a Config file + and using it. + +2004-06-09 Stephen Hemminger + + * Report rates in K=1000 (requested by several people) + * Add GNU long style options + * For HTB use get_hz to pick up value of system HZ at runtime + * Delete unused funcs. + +2004-06-08 Stephen Hemminger + + * Cleanup ss + - use const char and local functions where possible + * Add man pages from SuSe + * SuSE patches + - path to db4.1 + - don't hardcode path to /tmp in ifstat + Alternat fix: was to use TMPDIR + - handle non-root user calling ip route flush going into + an infinite loop. + Alternate fix: was to timeout if route table doesn't empty. + * Try and get rid of dependency on kernel include files + Get rid of having private glibc-include headers + +2004-06-07 Stephen Hemminger + + * Import patches that make sense from Fedora Core 2 + - iproute2-2.4.7-hex + print fwmark in hex + - iproute2-2.4.7-netlink + handle getting right netlink mesg back + - iproute2-2.4.7-htb3-tc + add HTB scheduler + - iproute2-2.4.7-default + add entry default to rttable + +2004-06-04 Stephen Hemminger + + * Add support for vegas info to ss + +2004-06-02 Stephen Hemminger + + * Use const char in utility routines where appropriate + * Rearrange include files so can build with standard headers + * For "tc qdisc ls" see the default queuing discpline "pfifo_fast" + and understand it + * Get rid of private defintions of network headers which existed + only to handle old glibc + +2004-04-15 Stephen Hemminger + + * Add the delay (network simulation scheduler) + +2004-04-15 Stephen Hemminger + + * Starting point baseline based on iproute2-2.4.7-ss020116-try + diff --git a/Config b/Config new file mode 100644 index 0000000..290e783 --- /dev/null +++ b/Config @@ -0,0 +1 @@ +# Generated config based on /tmp/iproute2-2.6.11/include diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d11462 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +DESTDIR= +SBINDIR=/usr/sbin +CONFDIR=/etc/iproute2 +DOCDIR=/usr/share/doc/iproute2 +MANDIR=/usr/share/man +KERNEL_INCLUDE=/usr/include + +# Path to db_185.h include +DBM_INCLUDE:=/usr/include + +DEFINES= -DRESOLVE_HOSTNAMES + +#options if you have a bind>=4.9.4 libresolv (or, maybe, glibc) +LDLIBS=-lresolv +ADDLIB= + +#options for decnet +ADDLIB+=dnet_ntop.o dnet_pton.o + +#options for ipx +ADDLIB+=ipx_ntop.o ipx_pton.o + +CC = gcc +HOSTCC = gcc +CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall +CFLAGS = $(CCOPTS) -I../include $(DEFINES) + +LDLIBS += -L../lib -lnetlink -lutil + +SUBDIRS=lib ip tc misc netem + +LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a + +all: Config + @for i in $(SUBDIRS); \ + do $(MAKE) $(MFLAGS) -C $$i; done + +Config: + ./configure $(KERNEL_INCLUDE) + +install: all + install -m 0755 -d $(DESTDIR)$(SBINDIR) + install -m 0755 -d $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv + install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples + install -m 0644 $(shell find examples/diffserv -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples/diffserv + @for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done + install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(MANDIR)/man8 + install -m 0644 $(shell find man/man8 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-bfifo.8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-pfifo.8 + install -m 0755 -d $(DESTDIR)$(MANDIR)/man3 + install -m 0644 $(shell find man/man3 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man3 + +clean: + @for i in $(SUBDIRS) doc; \ + do $(MAKE) $(MFLAGS) -C $$i clean; done + +clobber: clean + rm -f Config + +distclean: clean clobber + +.EXPORT_ALL_VARIABLES: diff --git a/Makefile.kernel b/Makefile.kernel new file mode 100644 index 0000000..abd5aab --- /dev/null +++ b/Makefile.kernel @@ -0,0 +1,67 @@ +DESTDIR= +SBINDIR=/usr/sbin +CONFDIR=/etc/iproute2 +DOCDIR=/usr/share/doc/iproute2 +MANDIR=/usr/share/man + +# Path to db_185.h include +DBM_INCLUDE:=/usr/include + +DEFINES= -DRESOLVE_HOSTNAMES + +#options if you have a bind>=4.9.4 libresolv (or, maybe, glibc) +LDLIBS=-lresolv +ADDLIB= + +#options for decnet +ADDLIB+=dnet_ntop.o dnet_pton.o + +#options for ipx +ADDLIB+=ipx_ntop.o ipx_pton.o + +CC = gcc +HOSTCC = gcc +CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall +CFLAGS = $(CCOPTS) -I../include $(DEFINES) + +LDLIBS += -L../lib -lnetlink -lutil + +SUBDIRS=lib ip tc misc netem + +LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a + +all: Config + @for i in $(SUBDIRS); \ + do $(MAKE) $(MFLAGS) -C $$i; done + +Config: + ./configure $(KERNEL_INCLUDE) + +install: all + install -m 0755 -d $(DESTDIR)$(SBINDIR) + install -m 0755 -d $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples + install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv + install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples + install -m 0644 $(shell find examples/diffserv -maxdepth 1 -type f) \ + $(DESTDIR)$(DOCDIR)/examples/diffserv + @for i in $(SUBDIRS) doc; do $(MAKE) -C $$i install; done + install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR) + install -m 0755 -d $(DESTDIR)$(MANDIR)/man8 + install -m 0644 $(shell find man/man8 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-bfifo.8 + ln -sf $(MANDIR)/man8/tc-pbfifo.8 $(DESTDIR)$(MANDIR)/man8/tc-pfifo.8 + install -m 0755 -d $(DESTDIR)$(MANDIR)/man3 + install -m 0644 $(shell find man/man3 -maxdepth 1 -type f) $(DESTDIR)$(MANDIR)/man3 + +clean: + @for i in $(SUBDIRS) doc; \ + do $(MAKE) $(MFLAGS) -C $$i clean; done + +clobber: clean + rm -f Config + +distclean: clean clobber + +.EXPORT_ALL_VARIABLES: diff --git a/README b/README new file mode 100644 index 0000000..d86f605 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Primary site is: + http://developer.osdl.org/dev/iproute2 + +Original FTP site is: + ftp://ftp.inr.ac.ru/ip-routing/ + +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. + +DBM_INCLUDE points to the directory with db_185.h which +is the include file used by arpd to get to the old format Berkely +database routines. Often this is in the db-devel package. + +2. make + +The makefile will automatically build a file Config which +contains whether or not ATM is available, etc. + +3. To make documentation, cd to doc/ directory , then + look at start of Makefile and set correct values for + PAGESIZE=a4 , ie: a4 , letter ... (string) + PAGESPERPAGE=2 , ie: 1 , 2 ... (numeric) + and make there. It assumes, that latex, dvips and psnup + are in your path. + +Stephen Hemminger +shemminger@osdl.org + +Alexey Kuznetsov +kuznet@ms2.inr.ac.ru diff --git a/README.decnet b/README.decnet new file mode 100644 index 0000000..4d7453a --- /dev/null +++ b/README.decnet @@ -0,0 +1,41 @@ + +Here are a few quick points about DECnet support... + + o No name resolution is available as yet, all addresses must be + entered numerically. + + o The neighbour cache may well list every entry as having the address + 0.170. This is due to a problem that I need to sort out kernel side. + It is harmless (but don't try and use neigh add yet) just look in + /proc/net/decnet_neigh to see the real addresses for now. + + o The rtnetlink support in the kernel is rather exprimental, expect a + few odd things to happen for the next few DECnet kernel releases. + + o Whilst you can use ip addr add to add more than one DECnet address to an + interface, don't expect addresses which are not the same as the + kernels node address to work properly. i.e. You will break the DECnet + protocol if you do add anything other than the automatically generated + interface addresses to ethernet cards. This option is there for future + link layer support, where the device will have to be configed for + DECnet explicitly. + + o The DECnet support is currently self contained. You do not need the + libdnet library to use it. In fact until I've sent the dnet_pton and + dnet_ntop functions to Patrick to add, you can't use libdnet. + + o If you are not using the very latest 2.3.xx series kernels, don't + try and list DECnet routes if you've got IPv6 compiled into the + kernel. It will oops. + + o My main reason for writing the DECnet support for iproute2 was to + check out the DECnet routing code, so the route get and + route show cache commands are likely to be the most debugged out of + all of them. + + o If you find bugs in the DECnet support, please send them to me in the + first instance, and then I'll send Alexey a patch to fix it. IPv4/6 + bugs should be sent to Alexey as before. + +Steve Whitehouse + diff --git a/README.distribution b/README.distribution new file mode 100644 index 0000000..fe78fb4 --- /dev/null +++ b/README.distribution @@ -0,0 +1,95 @@ +I. About the distribution tables + +The table used for "synthesizing" the distribution is essentially a scaled, +translated, inverse to the cumulative distribution function. + +Here's how to think about it: Let F() be the cumulative distribution +function for a probability distribution X. We'll assume we've scaled +things so that X has mean 0 and standard deviation 1, though that's not +so important here. Then: + + F(x) = P(X <= x) = \int_{-inf}^x f + +where f is the probability density function. + +F is monotonically increasing, so has an inverse function G, with range +0 to 1. Here, G(t) = the x such that P(X <= x) = t. (In general, G may +have singularities if X has point masses, i.e., points x such that +P(X = x) > 0.) + +Now we create a tabular representation of G as follows: Choose some table +size N, and for the ith entry, put in G(i/N). Let's call this table T. + +The claim now is, I can create a (discrete) random variable Y whose +distribution has the same approximate "shape" as X, simply by letting +Y = T(U), where U is a discrete uniform random variable with range 1 to N. +To see this, it's enough to show that Y's cumulative distribution function, +(let's call it H), is a discrete approximation to F. But + + H(x) = P(Y <= x) + = (# of entries in T <= x) / N -- as Y chosen uniformly from T + = i/N, where i is the largest integer such that G(i/N) <= x + = i/N, where i is the largest integer such that i/N <= F(x) + -- since G and F are inverse functions (and F is + increasing) + = floor(N*F(x))/N + +as desired. + +II. How to create distribution tables (in theory) + +How can we create this table in practice? In some cases, F may have a +simple expression which allows evaluating its inverse directly. The +pareto distribution is one example of this. In other cases, and +especially for matching an experimentally observed distribution, it's +easiest simply to create a table for F and "invert" it. Here, we give +a concrete example, namely how the new "experimental" distribution was +created. + +1. Collect enough data points to characterize the distribution. Here, I +collected 25,000 "ping" roundtrip times to a "distant" point (time.nist.gov). +That's far more data than is really necessary, but it was fairly painless to +collect it, so... + +2. Normalize the data so that it has mean 0 and standard deviation 1. + +3. Determine the cumulative distribution. The code I wrote creates a table +covering the range -10 to +10, with granularity .00005. Obviously, this +is absurdly over-precise, but since it's a one-time only computation, I +figured it hardly mattered. + +4. Invert the table: for each table entry F(x) = y, make the y*TABLESIZE +(here, 4096) entry be x*TABLEFACTOR (here, 8192). This creates a table +for the ("normalized") inverse of size TABLESIZE, covering its domain 0 +to 1 with granularity 1/TABLESIZE. Note that even with the granularity +used in creating the table for F, it's possible not all the entries in +the table for G will be filled in. So, make a pass through the +inverse's table, filling in any missing entries by linear interpolation. + +III. How to create distribution tables (in practice) + +If you want to do all this yourself, I've provided several tools to help: + +1. maketable does the steps 2-4 above, and then generates the appropriate +header file. So if you have your own time distribution, you can generate +the header simply by: + + maketable < time.values > header.h + +2. As explained in the other README file, the somewhat sleazy way I have +of generating correlated values needs correction. You can generate your +own correction tables by compiling makesigtable and makemutable with +your header file. Check the Makefile to see how this is done. + +3. Warning: maketable, makesigtable and especially makemutable do +enormous amounts of floating point arithmetic. Don't try running +these on an old 486. (NIST Net itself will run fine on such a +system, since in operation, it just needs to do a few simple integral +calculations. But getting there takes some work.) + +4. The tables produced are all normalized for mean 0 and standard +deviation 1. How do you know what values to use for real? Here, I've +provided a simple "stats" utility. Give it a series of floating point +values, and it will return their mean (mu), standard deviation (sigma), +and correlation coefficient (rho). You can then plug these values +directly into NIST Net. diff --git a/README.iproute2+tc b/README.iproute2+tc new file mode 100644 index 0000000..edd79c0 --- /dev/null +++ b/README.iproute2+tc @@ -0,0 +1,119 @@ +iproute2+tc* + +It's the first release of Linux traffic control engine. + + +NOTES. +* csz scheduler is inoperational at the moment, and probably + never will be repaired but replaced with h-pfq scheduler. +* To use "fw" classifier you will need ipfwchains patch. +* No manual available. Ask me, if you have problems (only try to guess + answer yourself at first 8)). + + +Micro-manual how to start it the first time +------------------------------------------- + +A. Attach CBQ to eth1: + +tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 cell 8 \ +avpkt 1000 mpu 64 + +B. Add root class: + +tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate 10Mbit \ +allot 1514 cell 8 weight 1Mbit prio 8 maxburst 20 avpkt 1000 + +C. Add default interactive class: + +tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit rate 1Mbit \ +allot 1514 cell 8 weight 100Kbit prio 3 maxburst 20 avpkt 1000 split 1:0 \ +defmap c0 + +D. Add default class: + +tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 8Mbit \ +allot 1514 cell 8 weight 800Kbit prio 7 maxburst 20 avpkt 1000 split 1:0 \ +defmap 3f + +etc. etc. etc. Well, it is enough to start 8) The rest can be guessed 8) +Look also at more elaborated example, ready to start rsvpd, +in rsvp/cbqinit.eth1. + + +Terminology and advices about setting CBQ parameters may be found in Sally Floyd +papers. + + +Pairs X:Y are class handles, X:0 are qdisc heandles. +weight should be proportional to rate for leaf classes +(I choosed it ten times less, but it is not necessary) + +defmap is bitmap of logical priorities served by this class. + +E. Another qdiscs are simpler. F.e. let's join TBF on class 1:2 + +tc qdisc add dev eth1 parent 1:2 tbf rate 64Kbit buffer 5Kb/8 limit 10Kb + +F. Look at all that we created: + +tc qdisc ls dev eth1 +tc class ls dev eth1 + +G. Install "route" classifier on root of cbq and map destination from realm +1 to class 1:2 + +tc filter add dev eth1 parent 1:0 protocol ip prio 100 route to 1 classid 1:2 + +H. Assign routes to 10.11.12.0/24 to realm 1 + +ip route add 10.11.12.0/24 dev eth1 via whatever realm 1 + +etc. The same thing can be made with rules. +I still did not test ipchains, but they should work too. + +Setup of rsvp and u32 classifiers is more hairy. +If you read RSVP specs, you will understand how rsvp classifier +works easily. What's about u32... That's example: + + + +#! /bin/sh + +TC=/home/root/tc + +# Setup classifier root on eth1 root (it is cbq) +$TC filter add dev eth1 parent 1:0 prio 5 protocol ip u32 + +# Create hash table of 256 slots with ID 1: +$TC filter add dev eth1 parent 1:0 prio 5 handle 1: u32 divisor 256 + +# Add to 6th slot of hash table rule to select tcp/telnet to 193.233.7.75 +# direct it to class 1:4 and prescribe to fall to best effort, +# if traffic violate TBF (32kbit,5K) +$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:6: \ + match ip dst 193.233.7.75 \ + match tcp dst 0x17 0xffff \ + flowid 1:4 \ + police rate 32kbit buffer 5kb/8 mpu 64 mtu 1514 index 1 + +# Add to 1th slot of hash table rule to select icmp to 193.233.7.75 +# direct it to class 1:4 and prescribe to fall to best effort, +# if traffic violate TBF (10kbit,5K) +$TC filter add dev eth1 parent 1:0 prio 5 u32 ht 1:: \ + sample ip protocol 1 0xff \ + match ip dst 193.233.7.75 \ + flowid 1:4 \ + police rate 10kbit buffer 5kb/8 mpu 64 mtu 1514 index 2 + +# Lookup hash table, if it is not fragmented frame +# Use protocol as hash key +$TC filter add dev eth1 parent 1:0 prio 5 handle ::1 u32 ht 800:: \ + match ip nofrag \ + offset mask 0x0F00 shift 6 \ + hashkey mask 0x00ff0000 at 8 \ + link 1: + + +Alexey Kuznetsov +kuznet@ms2.inr.ac.ru diff --git a/README.lnstat b/README.lnstat new file mode 100644 index 0000000..057925f --- /dev/null +++ b/README.lnstat @@ -0,0 +1,81 @@ +lnstat - linux networking statistics +(C) 2004 Harald Welte , various useful fixups: compilation + with old kernels, cross-compiling, "all" == "any" in prefix spec. + * Collected from my disk, cleaned and packed to directory iproute2/misc/ + several utilities: ss, nstat, ifstat, rtacct, arpd and module tcp_diag. + Writing some docs. me. + * prepared patchlet for pidentd to use tcp_diag. + * David Miller: 64bit (and even worse 64bit kernel/32 bit user :-) fixes + to above. tcp_diag is merged to main tree. + * Alexandr D. Kanevskiy : various flaws in ss + * Alexandr D. Kanevskiy : oops, more aggressive caching + of names opened old bugs: ip started to print garbage in some places. + * Robert Olsson, rt_cache_stat. Renamed to rtstat. + * An old bug in "ip maddr ls": reduntant empty lines in output. + Seeing this crap for ages but lucky match of desire/ability to repair + and a huff about this happened only today. :-) + * "Mr. James W. Laferriere" + doc: option to produce ps output for non-a4 and not only 2 pages/sheet. + * Jamal's patch for ingres qdisc. + * Bernd Eckenfels : deleted orphaned bogus #include + in include/utils.h. + * Julian Anastasov : uninitialized fields in nexthop + producing funny "dead" nexthops in multipath routes. + Stupid me, look at the first line in [010803]... Was it difficult to guess + this that time? People blame for several months. :-) + Special thanks to bert hubert who raised the issue in netdev. + Thanks and apologies to Terry Schmidt , + Ruben Puettmann , + Mark Ivens . + * willy tarreau : "make install" target. + * Tunable limit for sch_sfq. Patch to kernel activating this + is about to be submitted. Reminded by Adi Nugroho . + +[010824] + * ip address add sets scope of loopback addreses to "host". + Advised by David Miller. + * ZIP! and David Ford + Some strcpy's changed to strncpy's. + * David Ford , test for compilation with gcc3. + * David Ford . Damn, I broke rtnl_talk in previous + snapshot. + +[010803] + * If "dev" is not specified in multipath route, ifindex remained + uninitialized. Grr. Thanks to Kunihiro Ishiguro . + * Rafal Maszkowski , batch mode tc. The most old patch. + * Updates list of data protocol ids. + Lots of reporters. I bring my apologies. + * Jan Rekorajski . Updated list of datalink types. + * Christina Chen . Bug in parsing IPv6 address match in u32. + * Pekka Savola . ip -6 route flush dev lo stuck + on deleting root of the table. + * Werner. dsmark fixes. + * Alexander Demenshin . Old miracleous bug + in ip monitor. It was puzzle, people permanently blame that + it prints some crap. + * Rui Prior . f_route failed to resolve fromif. + Werner also noticed this and sent patch. Bad place... [RETHINK] + * Kim Woelders . + - changes in Makefile for cross-compile + - understand "all" as alias for "any" + - bug in iprule.c +! [ NB. Also he sent patch for kernel. Do not forget! ] + * Werner. Fix to tc core files: wrong exits etc. + * Bernd Jendrissek . Some sanitizations of tc.c +!* Marian Jancar . He say q_tbf prints wrong latency! +! Seems, he is wrong. + * Werner (and Nikolai Vladychevski ) check ->print_copts + to avoid segfault. + +[001007] + * Compiles under rh-7.0 + +[000928] + * Sorry. I have lost all the CVS with changes made since 000305. + If someone sent me a patch after this date, please, resubmit. + Restored from the last backup and mailboxes: + + * Edit ip-cref.tex by raf . + * RTAX_REORDERING support. + * IFLA_MASTER support. + * Bug in rtnl_talk(), libnetlink.c. Reported by David P. Olshfski + + +[000305] + * Bugs in RESOLVE_HOSTNAMES. Bratislav Ilich + * ARPHRD_IEEE802_TR + +[000225] + * ECN in q_red.c. + +[000221] + * diffserv update from Jamal Hadi Salim + * Some bits of IPX from Steve Whitehouse. + * ATM qdisc from Werner Almesberger + * Support for new attributes on routes in linux-2.3. + +[991023] + No news, only several bugs are fixed. + * Since ss990630 "ip rule list" printed wrong prefix length. + Vladimir V. Ivanov + * "ip rule" parsed >INT_MAX values of metric incorrectly. + Matthew G. Marsh + * Some improvements in doc/Makefile advised by + Andi Kleen and Werner Almesberger. + +[990824] + * new attributes in "ip route": rtt, rttvar, cwnd, ssthresh and advmss. + * some updates in documentaion to reflect new status. + +[990630] + * DiffServ support. + Werner Almesberger + Jamal Hadi Salim + * DECnet support. + Steve Whitehouse + * Some minor tweaks in docs and code. + +[990530] + * routel script. Stephen R. van den Berg + * Bug in tc/q_prio.c resetting priomap. Reported by + Ole Husgaard and + Jan Kasprzak + * IP command reference manual is published (ip-cref.tex). + I am sorry, but tc-cref.tex is still not ready, to be more + exact the draft does not describe current tc 8-) + * ip, rtmon, rtacct utilities are updated according to manual 8-) + Lots of changes: + - (MAIN) "flush" command for addr, neigh and route. + - error messages are sanitized; now it does not print + usage() page on each error. + - output format is improved. + - "oneline" mode is added. + - etc. + * Name databases; resolution acsii <-> numeric is split out to lib/* + * scripts ifcfg, ifone and rtpr. + * examples/dhcp-client-script is copied from my patch to ISC dhcp. + * Makefile in doc/ directory. + +[990417] + * "pmtudisc" flag to "ip tunnel". Phil Karn + * bug in tc/q_tbf.c preventing setting peak_rate, Martin Mares + * doc/flowlabels.tex + +[990329] + + * This snapshot fixes some compatibility problems, which I introduced + occasionally to previous snapshots. + * Namely, "allot" to "tc qdisc add ... cbq" is accepted but ignored. + * Another changes are supposed to be shown in the next snapshot, but + because of troubles with "allot" I am forced to release premature + version. Namely, "cell", "prio", "weight" etc. are optional now. + * doc/ip-tunnels.tex + +[990327] + * History was not recorded. + +[981002] + * Rani Assaf contributed resolving + addresses to names. + BEWARE! DO NOT USE THIS OPTION, WHEN REPORTING BUGS IN + IPROUTE OR IN KERENEL. ALL THE BUG REPORTS MUST CONTAIN + ONLY NUMERIC ADDRESSES. + +[981101] + * now it should compile for any libc. diff --git a/configure b/configure new file mode 100755 index 0000000..dc14e54 --- /dev/null +++ b/configure @@ -0,0 +1,27 @@ +#! /bin/bash +# This is not an autconf generated configure +# +INCLUDE=${1:-"$PWD/include"} + +echo "# Generated config based on" $INCLUDE >Config + +echo "TC schedulers" + +echo -n " ATM " +cat >/tmp/atmtest.c < +int main(int argc, char **argv) { + struct atm_qos qos; + (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); + return 0; +} +EOF +gcc -I$INCLUDE -o /tmp/atmtest /tmp/atmtest.c -latm >/dev/null 2>&1 +if [ $? -eq 0 ] +then + echo "TC_CONFIG_ATM:=y" >>Config + echo yes +else + echo no +fi +rm -f /tmp/atmtest.c /tmp/atmtest diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..84836a2 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,55 @@ +PSFILES=ip-cref.ps ip-tunnels.ps api-ip6-flowlabels.ps ss.ps nstat.ps arpd.ps rtstat.ps +# tc-cref.ps +# api-rtnl.tex api-pmtudisc.tex api-news.tex +# iki-netdev.ps iki-neighdst.ps + + +LATEX=latex +DVIPS=dvips +SGML2DVI=sgml2latex --output=dvi +SGML2HTML=sgml2html -s 0 +LPR=lpr -Zsduplex +SHELL=bash +PAGESIZE=a4 +PAGESPERPAGE=2 + +HTMLFILES=$(subst .sgml,.html,$(shell echo *.sgml)) +DVIFILES=$(subst .ps,.dvi,$(PSFILES)) + + +all: pstwocol + +pstwocol: $(PSFILES) + +html: $(HTMLFILES) + +dvi: $(DVIFILES) + +print: $(PSFILES) + $(LPR) $(PSFILES) + +%.dvi: %.sgml + $(SGML2DVI) $< + +%.dvi: %.tex + @set -e; pass=2; echo "Running LaTeX $<"; \ + while [ `$(LATEX) $< &1 | \ + grep -c '^\(LaTeX Warning: Label(s) may\|No file \|! Emergency stop\)'` -ge 1 ]; do \ + if [ $$pass -gt 3 ]; then \ + echo "Seems, something is wrong. Try by hands." ; exit 1 ; \ + fi; \ + echo "Re-running LaTeX $<, $${pass}d pass"; pass=$$[$$pass + 1]; \ + done + +%.ps: %.dvi + $(DVIPS) $< -o $@ + +%.html: %.sgml + $(SGML2HTML) $< + +install: + install -m 0644 $(shell echo *.tex) $(DESTDIR)$(DOCDIR) + install -m 0644 $(shell echo *.sgml) $(DESTDIR)$(DOCDIR) + +clean: + rm -f *.aux *.log *.toc $(PSFILES) $(DVIFILES) *.html diff --git a/doc/Plan b/doc/Plan new file mode 100644 index 0000000..55f478e --- /dev/null +++ b/doc/Plan @@ -0,0 +1,16 @@ +Partially finished work. + +1. User Reference manuals. +1.1 IP Command reference (ip-cref.tex, published) +1.2 TC Command reference (tc-cref.tex) +1.3 IP tunnels (ip-tunnels.tex, published) + +2. Linux-2.2 Networking API +2.1 RTNETLINK (api-rtnl.tex) +2.2 Path MTU Discovery (api-pmtudisc.tex) +2.3 IPv6 Flow Labels (api-ip6-flowlabels.tex, published) +2.4 Miscellaneous extensions (api-misc.tex) + +3. Linux-2.2 Networking Intra-Kernel Interfaces +3.1 NetDev --- Networking Devices and netdev... (iki-netdev.tex) +3.2 Neighbour cache and destination cache. (iki-neighdst.tex) diff --git a/doc/SNAPSHOT.tex b/doc/SNAPSHOT.tex new file mode 100644 index 0000000..7ed0298 --- /dev/null +++ b/doc/SNAPSHOT.tex @@ -0,0 +1 @@ +\def\Draft{020116} diff --git a/doc/actions/gact-usage b/doc/actions/gact-usage new file mode 100644 index 0000000..de1308d --- /dev/null +++ b/doc/actions/gact-usage @@ -0,0 +1,79 @@ + +gact [RAND] [INDEX] + +Where: + ACTION := reclassify | drop | continue | pass | ok + RAND := random + RANDTYPE := netrand | determ + VAL : = value not exceeding 10000 + INDEX := index value used + +ACTION semantics +- pass and ok are equivalent to accept +- continue allows to restart classification lookup +- drop drops packets +- reclassify implies continue classification where we left off + +randomization +-------------- + +At the moment there are only two algorithms. One is deterministic +and the other uses internal kernel netrand. + +Examples: + +Rules can be installed on both ingress and egress - this shows ingress +only + +tc qdisc add dev eth0 ingress + +# example 1 +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop + +ping -c 20 10.0.0.9 + +-- +filter u32 +filter u32 fh 800: ht divisor 1 +filter u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 32 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type none pass val 0 + index 1 ref 1 bind 1 installed 59 sec used 35 sec + Sent 1680 bytes 20 pkts (dropped 20, overlimits 0 ) + +---- + +# example 2 +#allow 1 out 10 randomly using the netrand generator +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random netrand ok 10 + +ping -c 20 10.0.0.9 + +---- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type netrand pass val 10 + index 5 ref 1 bind 1 installed 49 sec used 25 sec + Sent 1680 bytes 20 pkts (dropped 16, overlimits 0 ) + +-------- +#alternative: deterministically accept every second packet +tc filter add dev eth0 parent ffff: protocol ip prio 6 u32 match ip src \ +10.0.0.9/32 flowid 1:16 action drop random determ ok 2 + +ping -c 20 10.0.0.9 + +tc -s filter show parent ffff: dev eth0 +----- +filter protocol ip pref 6 u32 filter protocol ip pref 6 u32 fh 800: ht divisor 1filter protocol ip pref 6 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:16 (rule hit 20 success 20) + match 0a000009/ffffffff at 12 (success 20 ) + action order 1: gact action drop + random type determ pass val 2 + index 4 ref 1 bind 1 installed 118 sec used 82 sec + Sent 1680 bytes 20 pkts (dropped 10, overlimits 0 ) +----- + diff --git a/doc/actions/mirred-usage b/doc/actions/mirred-usage new file mode 100644 index 0000000..3e135a0 --- /dev/null +++ b/doc/actions/mirred-usage @@ -0,0 +1,71 @@ + +Very funky action. I do plan to add to a few more things to it +This is the basic stuff. Idea borrowed from the way ethernet switches +mirror and redirect packets. + +Usage: + +mirred [index INDEX] +where: +DIRECTION := +ACTION := +INDEX is the specific policy instance id +DEVICENAME is the devicename + + +Mirroring essentially takes a copy of the packet whereas redirecting +steals the packet and redirects to specified destination. + +Some examples: +Host A is hooked up to us on eth0 + +tc qdisc add dev lo ingress +# redirect all packets arriving on ingress of lo to eth0 +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress redirect dev eth0 + +On host A start a tcpdump on interface connecting to us. + +on our host ping -c 2 127.0.0.1 + +Ping would fail sinc all packets are heading out eth0 +tcpudmp on host A would show them + +if you substitute the redirect with mirror above as in: +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 action mirred egress mirror dev eth0 + +Then you should see the packets on both host A and the local +stack (i.e ping would work). + +Even more funky example: + +# +#allow 1 out 10 packets to randomly make it to the +# host A (Randomness uses the netrand generator) +# +tc filter add dev lo parent ffff: protocol ip prio 10 u32 \ +match u32 0 0 flowid 1:2 \ +action drop random determ ok 10\ +action mirred egress mirror dev eth0 + +------ +Example 2: +# for packets coming from 10.0.0.9: +#Redirect packets on egress (to ISP A) if you exceed a certain rate +# to eth1 (to ISP B) if you exceed a certain rate +# + +tc qdisc add dev eth0 handle 1:0 root prio + +tc filter add dev eth0 parent 1:0 protocol ip prio 6 u32 \ +match ip src 10.0.0.9/32 flowid 1:16 \ +action police rate 100kbit burst 90k ok \ +action mirred egress mirror dev eth1 + +--- + +A more interesting example is when you mirror flows to a dummy device +so you could tcpdump them (dummy by defaults drops all devices it sees). +This is a very useful debug feature. + diff --git a/doc/api-ip6-flowlabels.tex b/doc/api-ip6-flowlabels.tex new file mode 100644 index 0000000..aa34e94 --- /dev/null +++ b/doc/api-ip6-flowlabels.tex @@ -0,0 +1,429 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{IPv6 Flow Labels} +\input preamble +\begin{center} +\Large\bf IPv6 Flow Labels in Linux-2.2. +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm April 11, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + +\section{Introduction.} + +Every IPv6 packet carries 28 bits of flow information. RFC2460 splits +these bits to two fields: 8 bits of traffic class (or DS field, if you +prefer this term) and 20 bits of flow label. Currently there exist +no well-defined API to manage IPv6 flow information. In this document +I describe an attempt to design the API for Linux-2.2 IPv6 stack. + +\vskip 1mm + +The API must solve the following tasks: + +\begin{enumerate} + +\item To allow user to set traffic class bits. + +\item To allow user to read traffic class bits of received packets. +This feature is not so useful as the first one, however it will be +necessary f.e.\ to implement ECN [RFC2481] for datagram oriented services +or to implement receiver side of SRP or another end-to-end protocol +using traffic class bits. + +\item To assign flow labels to packets sent by user. + +\item To get flow labels of received packets. I do not know +any applications of this feature, but it is possible that receiver will +want to use flow labels to distinguish sub-flows. + +\item To allocate flow labels in the way, compliant to RFC2460. Namely: + +\begin{itemize} +\item +Flow labels must be uniformly distributed (pseudo-)random numbers, +so that any subset of 20 bits can be used as hash key. + +\item +Flows with coinciding source address and flow label must have identical +destination address and not-fragmentable extensions headers (i.e.\ +hop by hop options and all the headers up to and including routing header, +if it is present.) + +\begin{NB} +There is a hole in specs: some hop-by-hop options can be +defined only on per-packet base (f.e.\ jumbo payload option). +Essentially, it means that such options cannot present in packets +with flow labels. +\end{NB} +\begin{NB} +NB notes here and below reflect only my personal opinion, +they should be read with smile or should not be read at all :-). +\end{NB} + + +\item +Flow labels have finite lifetime and source is not allowed to reuse +flow label for another flow within the maximal lifetime has expired, +so that intermediate nodes will be able to invalidate flow state before +the label is taken over by another flow. +Flow state, including lifetime, is propagated along datagram path +by some application specific methods +(f.e.\ in RSVP PATH messages or in some hop-by-hop option). + + +\end{itemize} + +\end{enumerate} + +\section{Sending/receiving flow information.} + +\paragraph{Discussion.} +\addcontentsline{toc}{subsection}{Discussion} +It was proposed (Where? I do not remember any explicit statement) +to solve the first four tasks using +\verb|sin6_flowinfo| field added to \verb|struct| \verb|sockaddr_in6| +(see RFC2553). + +\begin{NB} + This method is difficult to consider as reasonable, because it + puts additional overhead to all the services, despite of only + very small subset of them (none, to be more exact) really use it. + It contradicts both to IETF spirit and the letter. Before RFC2553 + one justification existed, IPv6 address alignment left 4 byte + hole in \verb|sockaddr_in6| in any case. Now it has no justification. +\end{NB} + +We have two problems with this method. The first one is common for all OSes: +if \verb|recvmsg()| initializes \verb|sin6_flowinfo| to flow info +of received packet, we loose one very important property of BSD socket API, +namely, we are not allowed to use received address for reply directly +and have to mangle it, even if we are not interested in flowinfo subtleties. + +\begin{NB} + RFC2553 adds new requirement: to clear \verb|sin6_flowinfo|. + Certainly, it is not solution but rather attempt to force applications + to make unnecessary work. Well, as usually, one mistake in design + is followed by attempts to patch the hole and more mistakes... +\end{NB} + +Another problem is Linux specific. Historically Linux IPv6 did not +initialize \verb|sin6_flowinfo| at all, so that, if kernel does not +support flow labels, this field is not zero, but a random number. +Some applications also did not take care about it. + +\begin{NB} +Following RFC2553 such applications can be considered as broken, +but I still think that they are right: clearing all the address +before filling known fields is robust but stupid solution. +Useless wasting CPU cycles and +memory bandwidth is not a good idea. Such patches are acceptable +as temporary hacks, but not as standard of the future. +\end{NB} + + +\paragraph{Implementation.} +\addcontentsline{toc}{subsection}{Implementation} +By default Linux IPv6 does not read \verb|sin6_flowinfo| field +assuming that common applications are not obliged to initialize it +and are permitted to consider it as pure alignment padding. +In order to tell kernel that application +is aware of this field, it is necessary to set socket option +\verb|IPV6_FLOWINFO_SEND|. + +\begin{verbatim} + int on = 1; + setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO_SEND, + (void*)&on, sizeof(on)); +\end{verbatim} + +Linux kernel never fills \verb|sin6_flowinfo| field, when passing +message to user space, though the kernels which support flow labels +initialize it to zero. If user wants to get received flowinfo, he +will set option \verb|IPV6_FLOWINFO| and after this he will receive +flowinfo as ancillary data object of type \verb|IPV6_FLOWINFO| +(cf.\ RFC2292). + +\begin{verbatim} + int on = 1; + setsockopt(sock, SOL_IPV6, IPV6_FLOWINFO, (void*)&on, sizeof(on)); +\end{verbatim} + +Flowinfo received and latched by a connected TCP socket also may be fetched +with \verb|getsockopt()| \verb|IPV6_PKTOPTIONS| together with +another optional information. + +Besides that, in the spirit of RFC2292 the option \verb|IPV6_FLOWINFO| +may be used as alternative way to send flowinfo with \verb|sendmsg()| or +to latch it with \verb|IPV6_PKTOPTIONS|. + +\paragraph{Note about IPv6 options and destination address.} +\addcontentsline{toc}{subsection}{IPv6 options and destination address} +If \verb|sin6_flowinfo| does contain not zero flow label, +destination address in \verb|sin6_addr| and non-fragmentable +extension headers are ignored. Instead, kernel uses the values +cached at flow setup (see below). However, for connected sockets +kernel prefers the values set at connection time. + +\paragraph{Example.} +\addcontentsline{toc}{subsection}{Example} +After setting socket option \verb|IPV6_FLOWINFO| +flowlabel and DS field are received as ancillary data object +of type \verb|IPV6_FLOWINFO| and level \verb|SOL_IPV6|. +In the cases when it is convenient to use \verb|recvfrom(2)|, +it is possible to replace library variant with your own one, +sort of: + +\begin{verbatim} +#include +#include + +size_t recvfrom(int fd, char *buf, size_t len, int flags, + struct sockaddr *addr, int *addrlen) +{ + size_t cc; + char cbuf[128]; + struct cmsghdr *c; + struct iovec iov = { buf, len }; + struct msghdr msg = { addr, *addrlen, + &iov, 1, + cbuf, sizeof(cbuf), + 0 }; + + cc = recvmsg(fd, &msg, flags); + if (cc < 0) + return cc; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = 0; + *addrlen = msg.msg_namelen; + for (c=CMSG_FIRSTHDR(&msg); c; c = CMSG_NEXTHDR(&msg, c)) { + if (c->cmsg_level != SOL_IPV6 || + c->cmsg_type != IPV6_FLOWINFO) + continue; + ((struct sockaddr_in6*)addr)->sin6_flowinfo = *(__u32*)CMSG_DATA(c); + } + return cc; +} +\end{verbatim} + + + +\section{Flow label management.} + +\paragraph{Discussion.} +\addcontentsline{toc}{subsection}{Discussion} +Requirements of RFC2460 are pretty tough. Particularly, lifetimes +longer than boot time require to store allocated labels at stable +storage, so that the full implementation necessarily includes user space flow +label manager. There are at least three different approaches: + +\begin{enumerate} +\item {\bf ``Cooperative''. } We could leave flow label allocation wholly +to user space. When user needs label he requests manager directly. The approach +is valid, but as any ``cooperative'' approach it suffers of security problems. + +\begin{NB} +One idea is to disallow not privileged user to allocate flow +labels, but instead to pass the socket to manager via \verb|SCM_RIGHTS| +control message, so that it will allocate label and assign it to socket +itself. Hmm... the idea is interesting. +\end{NB} + +\item {\bf ``Indirect''.} Kernel redirects requests to user level daemon +and does not install label until the daemon acknowledged the request. +The approach is the most promising, it is especially pleasant to recognize +parallel with IPsec API [RFC2367,Craig]. Actually, it may share API with +IPsec. + +\item {\bf ``Stupid''.} To allocate labels in kernel space. It is the simplest +method, but it suffers of two serious flaws: the first, +we cannot lease labels with lifetimes longer than boot time, the second, +it is sensitive to DoS attacks. Kernel have to remember all the obsolete +labels until their expiration and malicious user may fastly eat all the +flow label space. + +\end{enumerate} + +Certainly, I choose the most ``stupid'' method. It is the cheapest one +for implementor (i.e.\ me), and taking into account that flow labels +still have no serious applications it is not useful to work on more +advanced API, especially, taking into account that eventually we +will get it for no fee together with IPsec. + + +\paragraph{Implementation.} +\addcontentsline{toc}{subsection}{Implementation} +Socket option \verb|IPV6_FLOWLABEL_MGR| allows to +request flow label manager to allocate new flow label, to reuse +already allocated one or to delete old flow label. +Its argument is \verb|struct| \verb|in6_flowlabel_req|: + +\begin{verbatim} +struct in6_flowlabel_req +{ + struct in6_addr flr_dst; + __u32 flr_label; + __u8 flr_action; + __u8 flr_share; + __u16 flr_flags; + __u16 flr_expires; + __u16 flr_linger; + __u32 __flr_reserved; + /* Options in format of IPV6_PKTOPTIONS */ +}; +\end{verbatim} + +\begin{itemize} + +\item \verb|dst| is IPv6 destination address associated with the label. + +\item \verb|label| is flow label value in network byte order. If it is zero, +kernel will allocate new pseudo-random number. Otherwise, kernel will try +to lease flow label ordered by user. In this case, it is user task to provide +necessary flow label randomness. + +\item \verb|action| is requested operation. Currently, only three operations +are defined: + +\begin{verbatim} +#define IPV6_FL_A_GET 0 /* Get flow label */ +#define IPV6_FL_A_PUT 1 /* Release flow label */ +#define IPV6_FL_A_RENEW 2 /* Update expire time */ +\end{verbatim} + +\item \verb|flags| are optional modifiers. Currently +only \verb|IPV6_FL_A_GET| has modifiers: + +\begin{verbatim} +#define IPV6_FL_F_CREATE 1 /* Allowed to create new label */ +#define IPV6_FL_F_EXCL 2 /* Do not create new label */ +\end{verbatim} + + +\item \verb|share| defines who is allowed to reuse the same flow label. + +\begin{verbatim} +#define IPV6_FL_S_NONE 0 /* Not defined */ +#define IPV6_FL_S_EXCL 1 /* Label is private */ +#define IPV6_FL_S_PROCESS 2 /* May be reused by this process */ +#define IPV6_FL_S_USER 3 /* May be reused by this user */ +#define IPV6_FL_S_ANY 255 /* Anyone may reuse it */ +\end{verbatim} + +\item \verb|linger| is time in seconds. After the last user releases flow +label, it will not be reused with different destination and options at least +during this time. If \verb|share| is not \verb|IPV6_FL_S_EXCL| the label +still can be shared by another sockets. Current implementation does not allow +unprivileged user to set linger longer than 60 sec. + +\item \verb|expires| is time in seconds. Flow label will be kept at least +for this time, but it will not be destroyed before user released it explicitly +or closed all the sockets using it. Current implementation does not allow +unprivileged user to set timeout longer than 60 sec. Proviledged applications +MAY set longer lifetimes, but in this case they MUST save allocated +labels at stable storage and restore them back after reboot before the first +application allocates new flow. + +\end{itemize} + +This structure is followed by optional extension headers associated +with this flow label in format of \verb|IPV6_PKTOPTIONS|. Only +\verb|IPV6_HOPOPTS|, \verb|IPV6_RTHDR| and, if \verb|IPV6_RTHDR| presents, +\verb|IPV6_DSTOPTS| are allowed. + +\paragraph{Example.} +\addcontentsline{toc}{subsection}{Example} + The function \verb|get_flow_label| allocates +private flow label. + +\begin{verbatim} +int get_flow_label(int fd, struct sockaddr_in6 *dst, __u32 fl) +{ + int on = 1; + struct in6_flowlabel_req freq; + + memset(&freq, 0, sizeof(freq)); + freq.flr_label = htonl(fl); + freq.flr_action = IPV6_FL_A_GET; + freq.flr_flags = IPV6_FL_F_CREATE | IPV6_FL_F_EXCL; + freq.flr_share = IPV6_FL_S_EXCL; + memcpy(&freq.flr_dst, &dst->sin6_addr, 16); + if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &freq, sizeof(freq)) == -1) { + perror ("can't lease flowlabel"); + return -1; + } + dst->sin6_flowinfo |= freq.flr_label; + + if (setsockopt(fd, SOL_IPV6, IPV6_FLOWINFO_SEND, + &on, sizeof(on)) == -1) { + perror ("can't send flowinfo"); + + freq.flr_action = IPV6_FL_A_PUT; + setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, + &freq, sizeof(freq)); + return -1; + } + return 0; +} +\end{verbatim} + +A bit more complicated example using routing header can be found +in \verb|ping6| utility (\verb|iputils| package). Linux rsvpd backend +contains an example of using operation \verb|IPV6_FL_A_RENEW|. + +\paragraph{Listing flow labels.} +\addcontentsline{toc}{subsection}{Listing flow labels} +List of currently allocated +flow labels may be read from \verb|/proc/net/ip6_flowlabel|. + +\begin{verbatim} +Label S Owner Users Linger Expires Dst Opt +A1BE5 1 0 0 6 3 3ffe2400000000010a0020fffe71fb30 0 +\end{verbatim} + +\begin{itemize} +\item \verb|Label| is hexadecimal flow label value. +\item \verb|S| is sharing style. +\item \verb|Owner| is ID of creator, it is zero, pid or uid, depending on + sharing style. +\item \verb|Users| is number of applications using the label now. +\item \verb|Linger| is \verb|linger| of this label in seconds. +\item \verb|Expires| is time until expiration of the label in seconds. It may + be negative, if the label is in use. +\item \verb|Dst| is IPv6 destination address. +\item \verb|Opt| is length of options, associated with the label. Option + data are not accessible. +\end{itemize} + + +\paragraph{Flow labels and RSVP.} +\addcontentsline{toc}{subsection}{Flow labels and RSVP} +RSVP daemon supports IPv6 flow labels +without any modifications to standard ISI RAPI. Sender must allocate +flow label, fill corresponding sender template and submit it to local rsvp +daemon. rsvpd will check the label and start to announce it in PATH +messages. Rsvpd on sender node will renew the flow label, so that it will not +be reused before path state expires and all the intermediate +routers and receiver purge flow state. + +\verb|rtap| utility is modified to parse flow labels. F.e.\ if user allocated +flow label \verb|0xA1234|, he may write: + +\begin{verbatim} +RTAP> sender 3ffe:2400::1/FL0xA1234 +\end{verbatim} + +Receiver makes reservation with command: +\begin{verbatim} +RTAP> reserve ff 3ffe:2400::1/FL0xA1234 +\end{verbatim} + +\end{document} diff --git a/doc/arpd.sgml b/doc/arpd.sgml new file mode 100644 index 0000000..0ab79c6 --- /dev/null +++ b/doc/arpd.sgml @@ -0,0 +1,130 @@ + + +
+ +ARPD Daemon +<author>Alexey Kuznetsov, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/arpd/ is daemon collecting gratuitous ARP information, saving +it on local disk and feeding it to kernel on demand to avoid +redundant broadcasting due to limited size of kernel ARP cache. +</abstract> + + +<p><bf/Description/ + +<p>The format of the command is: + +<tscreen><verb> + arpd OPTIONS [ INTERFACE [ INTERFACE ... ] ] +</verb></tscreen> + +<p> <tt/OPTIONS/ are: + +<itemize> + +<item><tt/-l/ - dump <tt/arpd/ database to stdout and exit. Output consists +of three columns: interface index, IP address and MAC address. +Negative entries for dead hosts are also shown, in this case MAC address +is replaced by word <tt/FAILED/ followed by colon and time when the fact +that host is dead was proven the last time. + +<item><tt/-f FILE/ - read and load <tt/arpd/ database from <tt/FILE/ +in text format similar dumped by option <tt/-l/. Exit after load, +probably listing resulting database, if option <tt/-l/ is also given. +If <tt/FILE/ is <tt/-/, <tt/stdin/ is read to get ARP table. + +<item><tt/-b DATABASE/ - location of database file. Default location is +<tt>/var/lib/arpd/arpd.db</tt>. + +<item><tt/-a NUMBER/ - <tt/arpd/ not only passively listens ARP on wire, but +also send brodcast queries itself. <tt/NUMBER/ is number of such queries +to make before destination is considered as dead. When <tt/arpd/ is started +as kernel helper (i.e. with <tt/app_solicit/ enabled in <tt/sysctl/ +or even with option <tt/-k/) without this option and still did not learn enough +information, you can observe 1 second gaps in service. Not fatal, but +not good. + +<item><tt/-k/ - suppress sending broadcast queries by kernel. It takes +sense together with option <tt/-a/. + +<item><tt/-n TIME/ - timeout of negative cache. When resolution fails <tt/arpd/ +suppresses further attempts to resolve for this period. It makes sense +only together with option <tt/-k/. This timeout should not be too much +longer than boot time of a typical host not supporting gratuitous ARP. +Default value is 60 seconds. + +<item><tt/-R RATE/ - maximal steady rate of broadcasts sent by <tt/arpd/ +in packets per second. Default value is 1. + +<item><tt/-B NUMBER/ - number of broadcasts sent by <tt/arpd/ back to back. +Default value is 3. Together with option <tt/-R/ this option allows +to police broadcasting not to exceed <tt/B+R*T/ over any interval +of time <tt/T/. + +</itemize> + +<p><tt/INTERFACE/ is name of networking inteface to watch. +If no interfaces given, <tt/arpd/ monitors all the interfaces. +In this case <tt/arpd/ does not adjust <tt/sysctl/ parameters, +it is supposed user does this himself after <tt/arpd/ is started. + + +<p> Signals + +<p> <tt/arpd/ exits gracefully syncing database and restoring adjusted +<tt/sysctl/ parameters, when receives <tt/SIGINT/ or <tt/SIGTERM/. +<tt/SIGHUP/ syncs database to disk. <tt/SIGUSR1/ sends some statistics +to <tt/syslog/. Effect of another signals is undefined, they may corrupt +database and leave <tt/sysctl/ parameters in an unpredictable state. + +<p> Note + +<p> In order to <tt/arpd/ be able to serve as ARP resolver, kernel must be +compiled with the option <tt/CONFIG_ARPD/ and, in the case when interface list +is not given on command line, variable <tt/app_solicit/ +on interfaces of interest should be set in <tt>/proc/sys/net/ipv4/neigh/*</tt>. +If this is not made <tt/arpd/ still collects gratuitous ARP information +in its database. + +<p> Examples + +<enum> +<item> Start <tt/arpd/ to collect gratuitous ARP, but not messing +with kernel functionality: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db +</verb></tscreen> + +<item> Look at result after some time: + +<tscreen><verb> + killall arpd + arpd -l -b /var/tmp/arpd.db +</verb></tscreen> + +<item> To enable kernel helper, leaving leading role to kernel: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db -a 1 eth0 eth1 +</verb></tscreen> + +<item> Completely replace kernel resolution on interfaces <tt/eth0/ +and <tt/eth1/. In this case kernel still does unicast probing to +validate entries, but all the broadcast activity is suppressed +and made under authority of <tt/arpd/: + +<tscreen><verb> + arpd -b /var/tmp/arpd.db -a 3 -k eth0 eth1 +</verb></tscreen> + +This is mode which <tt/arpd/ is supposed to work normally. +It is not default just to prevent occasional enabling of too aggressive +mode occasionally. + +</enum> + +</article> + diff --git a/doc/do-psnup b/doc/do-psnup new file mode 100755 index 0000000..2dce848 --- /dev/null +++ b/doc/do-psnup @@ -0,0 +1,16 @@ +#! /bin/bash +# $1 = Temporary file . "string" +# $2 = File to process . "string" +# $3 = Page size . ie: a4 , letter ... "string" +# $4 = Number of pages to fit on a single sheet . "numeric" + +if type psnup >&/dev/null; then + echo "psnup -$4 -p$3 $1 $2" + psnup -$4 -p$3 $1 $2 +elif type psmulti >&/dev/null; then + echo "psmulti $1 > $2" + psmulti $1 > $2 +else + echo "cp $1 $2" + cp $1 $2 +fi diff --git a/doc/ip-cref.tex b/doc/ip-cref.tex new file mode 100644 index 0000000..5eaa4a8 --- /dev/null +++ b/doc/ip-cref.tex @@ -0,0 +1,3316 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{IP Command Reference} +\input preamble +\begin{center} +\Large\bf IP Command Reference. +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm April 14, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + +\newpage + +\section{About this document} + +This document presents a comprehensive description of the \verb|ip| utility +from the \verb|iproute2| package. It is not a tutorial or user's guide. +It is a {\em dictionary\/}, not explaining terms, +but translating them into other terms, which may also be unknown to the reader. +However, the document is self-contained and the reader, provided they have a +basic networking background, will find enough information +and examples to understand and configure Linux-2.2 IP and IPv6 +networking. + +This document is split into sections explaining \verb|ip| commands +and options, decrypting \verb|ip| output and containing a few examples. +More voluminous examples and some topics, which require more elaborate +discussion, are in the appendix. + +The paragraphs beginning with NB contain side notes, warnings about +bugs and design drawbacks. They may be skipped at the first reading. + +\section{{\tt ip} --- command syntax} + +The generic form of an \verb|ip| command is: +\begin{verbatim} +ip [ OPTIONS ] OBJECT [ COMMAND [ ARGUMENTS ]] +\end{verbatim} +where \verb|OPTIONS| is a set of optional modifiers affecting the +general behaviour of the \verb|ip| utility or changing its output. All options +begin with the character \verb|'-'| and may be used in either long or abbreviated +forms. Currently, the following options are available: + +\begin{itemize} +\item \verb|-V|, \verb|-Version| + +--- print the version of the \verb|ip| utility and exit. + + +\item \verb|-s|, \verb|-stats|, \verb|-statistics| + +--- output more information. If the option +appears twice or more, the amount of information increases. +As a rule, the information is statistics or some time values. + + +\item \verb|-f|, \verb|-family| followed by a protocol family +identifier: \verb|inet|, \verb|inet6| or \verb|link|. + +--- enforce the protocol family to use. If the option is not present, +the protocol family is guessed from other arguments. If the rest of the command +line does not give enough information to guess the family, \verb|ip| falls back to the default +one, usually \verb|inet| or \verb|any|. \verb|link| is a special family +identifier meaning that no networking protocol is involved. + +\item \verb|-4| + +--- shortcut for \verb|-family inet|. + +\item \verb|-6| + +--- shortcut for \verb|-family inet6|. + +\item \verb|-0| + +--- shortcut for \verb|-family link|. + + +\item \verb|-o|, \verb|-oneline| + +--- output each record on a single line, replacing line feeds +with the \verb|'\'| character. This is convenient when you want to +count records with \verb|wc| or to \verb|grep| the output. The trivial +script \verb|rtpr| converts the output back into readable form. + +\item \verb|-r|, \verb|-resolve| + +--- use the system's name resolver to print DNS names instead of +host addresses. + +\begin{NB} + Do not use this option when reporting bugs or asking for advice. +\end{NB} +\begin{NB} + \verb|ip| never uses DNS to resolve names to addresses. +\end{NB} + +\end{itemize} + +\verb|OBJECT| is the object to manage or to get information about. +The object types currently understood by \verb|ip| are: + +\begin{itemize} +\item \verb|link| --- network device +\item \verb|address| --- protocol (IP or IPv6) address on a device +\item \verb|neighbour| --- ARP or NDISC cache entry +\item \verb|route| --- routing table entry +\item \verb|rule| --- rule in routing policy database +\item \verb|maddress| --- multicast address +\item \verb|mroute| --- multicast routing cache entry +\item \verb|tunnel| --- tunnel over IP +\end{itemize} + +Again, the names of all objects may be written in full or +abbreviated form, f.e.\ \verb|address| is abbreviated as \verb|addr| +or just \verb|a|. + +\verb|COMMAND| specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to \verb|add|, \verb|delete| and +\verb|show| (or \verb|list|) objects, but some objects +do not allow all of these operations or have some additional commands. +The \verb|help| command is available for all objects. It prints +out a list of available commands and argument syntax conventions. + +If no command is given, some default command is assumed. +Usually it is \verb|list| or, if the objects of this class +cannot be listed, \verb|help|. + +\verb|ARGUMENTS| is a list of arguments to the command. +The arguments depend on the command and object. There are two types of arguments: +{\em flags\/}, consisting of a single keyword, and {\em parameters\/}, +consisting of a keyword followed by a value. For convenience, +each command has some {\em default parameter\/} +which may be omitted. F.e.\ parameter \verb|dev| is the default +for the {\tt ip link} command, so {\tt ip link ls eth0} is equivalent +to {\tt ip link ls dev eth0}. +In the command descriptions below such parameters +are distinguished with the marker: ``(default)''. + +Almost all keywords may be abbreviated with several first (or even single) +letters. The shortcuts are convenient when \verb|ip| is used interactively, +but they are not recommended in scripts or when reporting bugs +or asking for advice. ``Officially'' allowed abbreviations are listed +in the document body. + + + +\section{{\tt ip} --- error messages} + +\verb|ip| may fail for one of the following reasons: + +\begin{itemize} +\item +A syntax error on the command line: an unknown keyword, incorrectly formatted +IP address {\em et al\/}. In this case \verb|ip| prints an error message +and exits. As a rule, the error message will contain information +about the reason for the failure. Sometimes it also prints a help page. + +\item +The arguments did not pass verification for self-consistency. + +\item +\verb|ip| failed to compile a kernel request from the arguments +because the user didn't give enough information. + +\item +The kernel returned an error to some syscall. In this case \verb|ip| +prints the error message, as it is output with \verb|perror(3)|, +prefixed with a comment and a syscall identifier. + +\item +The kernel returned an error to some RTNETLINK request. +In this case \verb|ip| prints the error message, as it is output +with \verb|perror(3)| prefixed with ``RTNETLINK answers:''. + +\end{itemize} + +All the operations are atomic, i.e.\ +if the \verb|ip| utility fails, it does not change anything +in the system. One harmful exception is \verb|ip link| command +(Sec.\ref{IP-LINK}, p.\pageref{IP-LINK}), +which may change only some of the device parameters given +on command line. + +It is difficult to list all the error messages (especially +syntax errors). However, as a rule, their meaning is clear +from the context of the command. + +The most common mistakes are: + +\begin{enumerate} +\item Netlink is not configured in the kernel. The message is: +\begin{verbatim} +Cannot open netlink socket: Invalid value +\end{verbatim} + +\item RTNETLINK is not configured in the kernel. In this case +one of the following messages may be printed, depending on the command: +\begin{verbatim} +Cannot talk to rtnetlink: Connection refused +Cannot send dump request: Connection refused +\end{verbatim} + +\item The \verb|CONFIG_IP_MULTIPLE_TABLES| option was not selected +when configuring the kernel. In this case any attempt to use the +\verb|ip| \verb|rule| command will fail, f.e. +\begin{verbatim} +kuznet@kaiser $ ip rule list +RTNETLINK error: Invalid argument +dump terminated +\end{verbatim} + +\end{enumerate} + + +\section{{\tt ip link} --- network device configuration} +\label{IP-LINK} + +\paragraph{Object:} A \verb|link| is a network device and the corresponding +commands display and change the state of devices. + +\paragraph{Commands:} \verb|set| and \verb|show| (or \verb|list|). + +\subsection{{\tt ip link set} --- change device attributes} + +\paragraph{Abbreviations:} \verb|set|, \verb|s|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| (default) + +--- \verb|NAME| specifies the network device on which to operate. + +\item \verb|up| and \verb|down| + +--- change the state of the device to \verb|UP| or \verb|DOWN|. + +\item \verb|arp on| or \verb|arp off| + +--- change the \verb|NOARP| flag on the device. + +\begin{NB} +This operation is {\em not allowed\/} if the device is in state \verb|UP|. +Though neither the \verb|ip| utility nor the kernel check for this condition. +You can get unpredictable results changing this flag while the +device is running. +\end{NB} + +\item \verb|multicast on| or \verb|multicast off| + +--- change the \verb|MULTICAST| flag on the device. + +\item \verb|dynamic on| or \verb|dynamic off| + +--- change the \verb|DYNAMIC| flag on the device. + +\item \verb|name NAME| + +--- change the name of the device. This operation is not +recommended if the device is running or has some addresses +already configured. + +\item \verb|txqueuelen NUMBER| or \verb|txqlen NUMBER| + +--- change the transmit queue length of the device. + +\item \verb|mtu NUMBER| + +--- change the MTU of the device. + +\item \verb|address LLADDRESS| + +--- change the station address of the interface. + +\item \verb|broadcast LLADDRESS|, \verb|brd LLADDRESS| or \verb|peer LLADDRESS| + +--- change the link layer broadcast address or the peer address when +the interface is \verb|POINTOPOINT|. + +\vskip 1mm +\begin{NB} +For most devices (f.e.\ for Ethernet) changing the link layer +broadcast address will break networking. +Do not use it, if you do not understand what this operation really does. +\end{NB} + +\end{itemize} + +\vskip 1mm +\begin{NB} +The {\tt ip} utility does not change the \verb|PROMISC| +or \verb|ALLMULTI| flags. These flags are considered +obsolete and should not be changed administratively. +\end{NB} + +\paragraph{Warning:} If multiple parameter changes are requested, +\verb|ip| aborts immediately after any of the changes have failed. +This is the only case when \verb|ip| can move the system to +an unpredictable state. The solution is to avoid changing +several parameters with one {\tt ip link set} call. + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip link set dummy address 00:00:00:00:00:01| + +--- change the station address of the interface \verb|dummy|. + +\item \verb|ip link set dummy up| + +--- start the interface \verb|dummy|. + +\end{itemize} + + +\subsection{{\tt ip link show} --- display device attributes} +\label{IP-LINK-SHOW} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|, +\verb|l|. + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|dev NAME| (default) + +--- \verb|NAME| specifies the network device to show. +If this argument is omitted all devices are listed. + +\item \verb|up| + +--- only display running interfaces. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff +kuznet@alisa:~ $ ip link ls sit0 +5: sit0@NONE: <NOARP,UP> mtu 1480 qdisc noqueue + link/sit 0.0.0.0 brd 0.0.0.0 +kuznet@alisa:~ $ ip link ls dummy +2: dummy: <BROADCAST,NOARP> mtu 1500 qdisc noop + link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff +kuznet@alisa:~ $ +\end{verbatim} + + +The number before each colon is an {\em interface index\/} or {\em ifindex\/}. +This number uniquely identifies the interface. This is followed by the {\em interface name\/} +(\verb|eth0|, \verb|sit0| etc.). The interface name is also +unique at every given moment. However, the interface may disappear from the +list (f.e.\ when the corresponding driver module is unloaded) and another +one with the same name may be created later. Besides that, +the administrator may change the name of any device with +\verb|ip| \verb|link| \verb|set| \verb|name| +to make it more intelligible. + +The interface name may have another name or \verb|NONE| appended +after the \verb|@| sign. This means that this device is bound to some other +device, +i.e.\ packets send through it are encapsulated and sent via the ``master'' +device. If the name is \verb|NONE|, the master is unknown. + +Then we see the interface {\em mtu\/} (``maximal transfer unit''). This determines +the maximal size of data which can be sent as a single packet over this interface. + +{\em qdisc\/} (``queuing discipline'') shows the queuing algorithm used +on the interface. Particularly, \verb|noqueue| means that this interface +does not queue anything and \verb|noop| means that the interface is in blackhole +mode i.e.\ all packets sent to it are immediately discarded. +{\em qlen\/} is the default transmit queue length of the device measured +in packets. + +The interface flags are summarized in the angle brackets. + +\begin{itemize} +\item \verb|UP| --- the device is turned on. It is ready to accept +packets for transmission and it may inject into the kernel packets received +from other nodes on the network. + +\item \verb|LOOPBACK| --- the interface does not communicate with other +hosts. All packets sent through it will be returned +and nothing but bounced packets can be received. + +\item \verb|BROADCAST| --- the device has the facility to send packets +to all hosts sharing the same link. A typical example is an Ethernet link. + +\item \verb|POINTOPOINT| --- the link has only two ends with one node +attached to each end. All packets sent to this link will reach the peer +and all packets received by us came from this single peer. + +If neither \verb|LOOPBACK| nor \verb|BROADCAST| nor \verb|POINTOPOINT| +are set, the interface is assumed to be NMBA (Non-Broadcast Multi-Access). +This is the most generic type of device and the most complicated one, because +the host attached to a NBMA link has no means to send to anyone +without additionally configured information. + +\item \verb|MULTICAST| --- is an advisory flag indicating that the interface +is aware of multicasting i.e.\ sending packets to some subset of neighbouring +nodes. Broadcasting is a particular case of multicasting, where the multicast +group consists of all nodes on the link. It is important to emphasize +that software {\em must not\/} interpret the absence of this flag as the inability +to use multicasting on this interface. Any \verb|POINTOPOINT| and +\verb|BROADCAST| link is multicasting by definition, because we have +direct access to all the neighbours and, hence, to any part of them. +Certainly, the use of high bandwidth multicast transfers is not recommended +on broadcast-only links because of high expense, but it is not strictly +prohibited. + +\item \verb|PROMISC| --- the device listens to and feeds to the kernel all +traffic on the link even if it is not destined for us, not broadcasted +and not destined for a multicast group of which we are member. Usually +this mode exists only on broadcast links and is used by bridges and for network +monitoring. + +\item \verb|ALLMULTI| --- the device receives all multicast packets +wandering on the link. This mode is used by multicast routers. + +\item \verb|NOARP| --- this flag is different from the other ones. It has +no invariant value and its interpretation depends on the network protocols +involved. As a rule, it indicates that the device needs no address +resolution and that the software or hardware knows how to deliver packets +without any help from the protocol stacks. + +\item \verb|DYNAMIC| --- is an advisory flag indicating that the interface is +dynamically created and destroyed. + +\item \verb|SLAVE| --- this interface is bonded to some other interfaces +to share link capacities. + +\end{itemize} + +\vskip 1mm +\begin{NB} +There are other flags but they are either obsolete (\verb|NOTRAILERS|) +or not implemented (\verb|DEBUG|) or specific to some devices +(\verb|MASTER|, \verb|AUTOMEDIA| and \verb|PORTSEL|). We do not discuss +them here. +\end{NB} +\begin{NB} +The values of \verb|PROMISC| and \verb|ALLMULTI| flags +shown by the \verb|ifconfig| utility and by the \verb|ip| utility +are {\em different\/}. \verb|ip link ls| shows the true device state, +while \verb|ifconfig| shows the virtual state which was set with +\verb|ifconfig| itself. +\end{NB} + + +The second line contains information on the link layer addresses +associated with the device. The first word (\verb|ether|, \verb|sit|) +defines the interface hardware type. This type determines the format and semantics +of the addresses and is logically part of the address. +The default format of the station address and the broadcast address +(or the peer address for pointopoint links) is a +sequence of hexadecimal bytes separated by colons, but some link +types may have their natural address format, f.e.\ addresses +of tunnels over IP are printed as dotted-quad IP addresses. + +\vskip 1mm +\begin{NB} + NBMA links have no well-defined broadcast or peer address, + however this field may contain useful information, f.e.\ + about the address of broadcast relay or about the address of the ARP server. +\end{NB} +\begin{NB} +Multicast addresses are not shown by this command, see +\verb|ip maddr ls| in~Sec.\ref{IP-MADDR} (p.\pageref{IP-MADDR} of this +document). +\end{NB} + + +\paragraph{Statistics:} With the \verb|-statistics| option, \verb|ip| also +prints interface statistics: + +\begin{verbatim} +kuznet@alisa:~ $ ip -s link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + RX: bytes packets errors dropped overrun mcast + 2449949362 2786187 0 0 0 0 + TX: bytes packets errors dropped carrier collsns + 178558497 1783945 332 0 332 35172 +kuznet@alisa:~ $ +\end{verbatim} +\verb|RX:| and \verb|TX:| lines summarize receiver and transmitter +statistics. They contain: +\begin{itemize} +\item \verb|bytes| --- the total number of bytes received or transmitted +on the interface. This number wraps when the maximal length of the data type +natural for the architecture is exceeded, so continuous monitoring requires +a user level daemon snapping it periodically. +\item \verb|packets| --- the total number of packets received or transmitted +on the interface. +\item \verb|errors| --- the total number of receiver or transmitter errors. +\item \verb|dropped| --- the total number of packets dropped due to lack +of resources. +\item \verb|overrun| --- the total number of receiver overruns resulting +in dropped packets. As a rule, if the interface is overrun, it means +serious problems in the kernel or that your machine is too slow +for this interface. +\item \verb|mcast| --- the total number of received multicast packets. This option +is only supported by a few devices. +\item \verb|carrier| --- total number of link media failures f.e.\ because +of lost carrier. +\item \verb|collsns| --- the total number of collision events +on Ethernet-like media. This number may have a different sense on other +link types. +\item \verb|compressed| --- the total number of compressed packets. This is +available only for links using VJ header compression. +\end{itemize} + + +If the \verb|-s| option is entered twice or more, +\verb|ip| prints more detailed statistics on receiver +and transmitter errors. + +\begin{verbatim} +kuznet@alisa:~ $ ip -s -s link ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + RX: bytes packets errors dropped overrun mcast + 2449949362 2786187 0 0 0 0 + RX errors: length crc frame fifo missed + 0 0 0 0 0 + TX: bytes packets errors dropped carrier collsns + 178558497 1783945 332 0 332 35172 + TX errors: aborted fifo window heartbeat + 0 0 0 332 +kuznet@alisa:~ $ +\end{verbatim} +These error names are pure Ethernetisms. Other devices +may have non zero values in these fields but they may be +interpreted differently. + + +\section{{\tt ip address} --- protocol address management} + +\paragraph{Abbreviations:} \verb|address|, \verb|addr|, \verb|a|. + +\paragraph{Object:} The \verb|address| is a protocol (IP or IPv6) address attached +to a network device. Each device must have at least one address +to use the corresponding protocol. It is possible to have several +different addresses attached to one device. These addresses are not +discriminated, so that the term {\em alias\/} is not quite appropriate +for them and we do not use it in this document. + +The \verb|ip addr| command displays addresses and their properties, +adds new addresses and deletes old ones. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|flush| and \verb|show| +(or \verb|list|). + + +\subsection{{\tt ip address add} --- add a new protocol address} +\label{IP-ADDR-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| + +\noindent--- the name of the device to add the address to. + +\item \verb|local ADDRESS| (default) + +--- the address of the interface. The format of the address depends +on the protocol. It is a dotted quad for IP and a sequence of hexadecimal halfwords +separated by colons for IPv6. The \verb|ADDRESS| may be followed by +a slash and a decimal number which encodes the network prefix length. + + +\item \verb|peer ADDRESS| + +--- the address of the remote endpoint for pointopoint interfaces. +Again, the \verb|ADDRESS| may be followed by a slash and a decimal number, +encoding the network prefix length. If a peer address is specified, +the local address {\em cannot\/} have a prefix length. The network prefix is associated +with the peer rather than with the local address. + + +\item \verb|broadcast ADDRESS| + +--- the broadcast address on the interface. + +It is possible to use the special symbols \verb|'+'| and \verb|'-'| +instead of the broadcast address. In this case, the broadcast address +is derived by setting/resetting the host bits of the interface prefix. + +\vskip 1mm +\begin{NB} +Unlike \verb|ifconfig|, the \verb|ip| utility {\em does not\/} set any broadcast +address unless explicitly requested. +\end{NB} + + +\item \verb|label NAME| + +--- Each address may be tagged with a label string. +In order to preserve compatibility with Linux-2.0 net aliases, +this string must coincide with the name of the device or must be prefixed +with the device name followed by colon. + + +\item \verb|scope SCOPE_VALUE| + +--- the scope of the area where this address is valid. +The available scopes are listed in file \verb|/etc/iproute2/rt_scopes|. +Predefined scope values are: + + \begin{itemize} + \item \verb|global| --- the address is globally valid. + \item \verb|site| --- (IPv6 only) the address is site local, + i.e.\ it is valid inside this site. + \item \verb|link| --- the address is link local, i.e.\ + it is valid only on this device. + \item \verb|host| --- the address is valid only inside this host. + \end{itemize} + +Appendix~\ref{ADDR-SEL} (p.\pageref{ADDR-SEL} of this document) +contains more details on address scopes. + +\end{itemize} + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip addr add 127.0.0.1/8 dev lo brd + scope host| + +--- add the usual loopback address to the loopback device. + +\item \verb|ip addr add 10.0.0.1/24 brd + dev eth0 label eth0:Alias| + +--- add the address 10.0.0.1 with prefix length 24 (i.e.\ netmask +\verb|255.255.255.0|), standard broadcast and label \verb|eth0:Alias| +to the interface \verb|eth0|. +\end{itemize} + + +\subsection{{\tt ip address delete} --- delete a protocol address} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Arguments:} coincide with the arguments of \verb|ip addr add|. +The device name is a required argument. The rest are optional. +If no arguments are given, the first address is deleted. + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip addr del 127.0.0.1/8 dev lo| + +--- deletes the loopback address from the loopback device. +It would be best not to repeat this experiment. + +\item Disable IP on the interface \verb|eth0|: +\begin{verbatim} + while ip -f inet addr del dev eth0; do + : nothing + done +\end{verbatim} +Another method to disable IP on an interface using {\tt ip addr flush} +may be found in sec.\ref{IP-ADDR-FLUSH}, p.\pageref{IP-ADDR-FLUSH}. + +\end{itemize} + + +\subsection{{\tt ip address show} --- display protocol addresses} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|lst|, \verb|sh|, \verb|ls|, +\verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|dev NAME| (default) + +--- the name of the device. + +\item \verb|scope SCOPE_VAL| + +--- only list addresses with this scope. + +\item \verb|to PREFIX| + +--- only list addresses matching this prefix. + +\item \verb|label PATTERN| + +--- only list addresses with labels matching the \verb|PATTERN|. +\verb|PATTERN| is a usual shell style pattern. + + +\item \verb|dynamic| and \verb|permanent| + +--- (IPv6 only) only list addresses installed due to stateless +address configuration or only list permanent (not dynamic) addresses. + +\item \verb|tentative| + +--- (IPv6 only) only list addresses which did not pass duplicate +address detection. + +\item \verb|deprecated| + +--- (IPv6 only) only list deprecated addresses. + + +\item \verb|primary| and \verb|secondary| + +--- only list primary (or secondary) addresses. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip addr ls eth0 +3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc cbq qlen 100 + link/ether 00:a0:cc:66:18:78 brd ff:ff:ff:ff:ff:ff + inet 193.233.7.90/24 brd 193.233.7.255 scope global eth0 + inet6 3ffe:2400:0:1:2a0:ccff:fe66:1878/64 scope global dynamic + valid_lft forever preferred_lft 604746sec + inet6 fe80::2a0:ccff:fe66:1878/10 scope link +kuznet@alisa:~ $ +\end{verbatim} + +The first two lines coincide with the output of \verb|ip link ls|. +It is natural to interpret link layer addresses +as addresses of the protocol family \verb|AF_PACKET|. + +Then the list of IP and IPv6 addresses follows, accompanied by +additional address attributes: scope value (see Sec.\ref{IP-ADDR-ADD}, +p.\pageref{IP-ADDR-ADD} above), flags and the address label. + +Address flags are set by the kernel and cannot be changed +administratively. Currently, the following flags are defined: + +\begin{enumerate} +\item \verb|secondary| + +--- the address is not used when selecting the default source address +of outgoing packets (Cf.\ Appendix~\ref{ADDR-SEL}, p.\pageref{ADDR-SEL}.). +An IP address becomes secondary if another address with the same +prefix bits already exists. The first address is primary. +It is the leader of the group of all secondary addresses. When the leader +is deleted, all secondaries are purged too. + + +\item \verb|dynamic| + +--- the address was created due to stateless autoconfiguration~\cite{RFC-ADDRCONF}. +In this case the output also contains information on times, when +the address is still valid. After \verb|preferred_lft| expires the address is +moved to the deprecated state. After \verb|valid_lft| expires the address +is finally invalidated. + +\item \verb|deprecated| + +--- the address is deprecated, i.e.\ it is still valid, but cannot +be used by newly created connections. + +\item \verb|tentative| + +--- the address is not used because duplicate address detection~\cite{RFC-ADDRCONF} +is still not complete or failed. + +\end{enumerate} + + +\subsection{{\tt ip address flush} --- flush protocol addresses} +\label{IP-ADDR-FLUSH} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:}This command flushes the protocol addresses +selected by some criteria. + +\paragraph{Arguments:} This command has the same arguments as \verb|show|. +The difference is that it does not run when no arguments are given. + +\paragraph{Warning:} This command (and other \verb|flush| commands +described below) is pretty dangerous. If you make a mistake, it will +not forgive it, but will cruelly purge all the addresses. + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted addresses and the number +of rounds made to flush the address list. If this option is given +twice, \verb|ip addr flush| also dumps all the deleted addresses +in the format described in the previous subsection. + +\paragraph{Example:} Delete all the addresses from the private network +10.0.0.0/8: +\begin{verbatim} +netadm@amber:~ # ip -s -s a f to 10/8 +2: dummy inet 10.7.7.7/16 brd 10.7.255.255 scope global dummy +3: eth0 inet 10.10.7.7/16 brd 10.10.255.255 scope global eth0 +4: eth1 inet 10.8.7.7/16 brd 10.8.255.255 scope global eth1 + +*** Round 1, deleting 3 addresses *** +*** Flush is complete after 1 round *** +netadm@amber:~ # +\end{verbatim} +Another instructive example is disabling IP on all the Ethernets: +\begin{verbatim} +netadm@amber:~ # ip -4 addr flush label "eth*" +\end{verbatim} +And the last example shows how to flush all the IPv6 addresses +acquired by the host from stateless address autoconfiguration +after you enabled forwarding or disabled autoconfiguration. +\begin{verbatim} +netadm@amber:~ # ip -6 addr flush dynamic +\end{verbatim} + + + +\section{{\tt ip neighbour} --- neighbour/arp tables management} + +\paragraph{Abbreviations:} \verb|neighbour|, \verb|neighbor|, \verb|neigh|, +\verb|n|. + +\paragraph{Object:} \verb|neighbour| objects establish bindings between protocol +addresses and link layer addresses for hosts sharing the same link. +Neighbour entries are organized into tables. The IPv4 neighbour table +is known by another name --- the ARP table. + +The corresponding commands display neighbour bindings +and their properties, add new neighbour entries and delete old ones. + +\paragraph{Commands:} \verb|add|, \verb|change|, \verb|replace|, +\verb|delete|, \verb|flush| and \verb|show| (or \verb|list|). + +\paragraph{See also:} Appendix~\ref{PROXY-NEIGH}, p.\pageref{PROXY-NEIGH} +describes how to manage proxy ARP/NDISC with the \verb|ip| utility. + + +\subsection{{\tt ip neighbour add} --- add a new neighbour entry\\ + {\tt ip neighbour change} --- change an existing entry\\ + {\tt ip neighbour replace} --- add a new entry or change an existing one} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; +\verb|replace|, \verb|repl|. + +\paragraph{Description:} These commands create new neighbour records +or update existing ones. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|to ADDRESS| (default) + +--- the protocol address of the neighbour. It is either an IPv4 or IPv6 address. + +\item \verb|dev NAME| + +--- the interface to which this neighbour is attached. + + +\item \verb|lladdr LLADDRESS| + +--- the link layer address of the neighbour. \verb|LLADDRESS| can also be +\verb|null|. + +\item \verb|nud NUD_STATE| + +--- the state of the neighbour entry. \verb|nud| is an abbreviation for ``Neighbour +Unreachability Detection''. The state can take one of the following values: + +\begin{enumerate} +\item \verb|permanent| --- the neighbour entry is valid forever and can be only be removed +administratively. +\item \verb|noarp| --- the neighbour entry is valid. No attempts to validate +this entry will be made but it can be removed when its lifetime expires. +\item \verb|reachable| --- the neighbour entry is valid until the reachability +timeout expires. +\item \verb|stale| --- the neighbour entry is valid but suspicious. +This option to \verb|ip neigh| does not change the neighbour state if +it was valid and the address is not changed by this command. +\end{enumerate} + +\end{itemize} + +\paragraph{Examples:} +\begin{itemize} +\item \verb|ip neigh add 10.0.0.3 lladdr 0:0:0:0:0:1 dev eth0 nud perm| + +--- add a permanent ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|. + +\item \verb|ip neigh chg 10.0.0.3 dev eth0 nud reachable| + +--- change its state to \verb|reachable|. +\end{itemize} + + +\subsection{{\tt ip neighbour delete} --- delete a neighbour entry} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Description:} This command invalidates a neighbour entry. + +\paragraph{Arguments:} The arguments are the same as with \verb|ip neigh add|, +except that \verb|lladdr| and \verb|nud| are ignored. + + +\paragraph{Example:} +\begin{itemize} +\item \verb|ip neigh del 10.0.0.3 dev eth0| + +--- invalidate an ARP entry for the neighbour 10.0.0.3 on the device \verb|eth0|. + +\end{itemize} + +\begin{NB} + The deleted neighbour entry will not disappear from the tables + immediately. If it is in use it cannot be deleted until the last + client releases it. Otherwise it will be destroyed during + the next garbage collection. +\end{NB} + + +\paragraph{Warning:} Attempts to delete or manually change +a \verb|noarp| entry created by the kernel may result in unpredictable behaviour. +Particularly, the kernel may try to resolve this address even +on a \verb|NOARP| interface or if the address is multicast or broadcast. + + +\subsection{{\tt ip neighbour show} --- list neighbour entries} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|. + +\paragraph{Description:}This commands displays neighbour tables. + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|to ADDRESS| (default) + +--- the prefix selecting the neighbours to list. + +\item \verb|dev NAME| + +--- only list the neighbours attached to this device. + +\item \verb|unused| + +--- only list neighbours which are not currently in use. + +\item \verb|nud NUD_STATE| + +--- only list neighbour entries in this state. \verb|NUD_STATE| takes +values listed below or the special value \verb|all| which means all states. +This option may occur more than once. If this option is absent, \verb|ip| +lists all entries except for \verb|none| and \verb|noarp|. + +\end{itemize} + + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip neigh ls +:: dev lo lladdr 00:00:00:00:00:00 nud noarp +fe80::200:cff:fe76:3f85 dev eth0 lladdr 00:00:0c:76:3f:85 router \ + nud stale +0.0.0.0 dev lo lladdr 00:00:00:00:00:00 nud noarp +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 nud reachable +193.233.7.85 dev eth0 lladdr 00:e0:1e:63:39:00 nud stale +kuznet@alisa:~ $ +\end{verbatim} + +The first word of each line is the protocol address of the neighbour. +Then the device name follows. The rest of the line describes the contents of +the neighbour entry identified by the pair (device, address). + +\verb|lladdr| is the link layer address of the neighbour. + +\verb|nud| is the state of the ``neighbour unreachability detection'' machine +for this entry. The detailed description of the neighbour +state machine can be found in~\cite{RFC-NDISC}. Here is the full list +of the states with short descriptions: + +\begin{enumerate} +\item\verb|none| --- the state of the neighbour is void. +\item\verb|incomplete| --- the neighbour is in the process of resolution. +\item\verb|reachable| --- the neighbour is valid and apparently reachable. +\item\verb|stale| --- the neighbour is valid, but is probably already +unreachable, so the kernel will try to check it at the first transmission. +\item\verb|delay| --- a packet has been sent to the stale neighbour and the kernel is waiting +for confirmation. +\item\verb|probe| --- the delay timer expired but no confirmation was received. +The kernel has started to probe the neighbour with ARP/NDISC messages. +\item\verb|failed| --- resolution has failed. +\item\verb|noarp| --- the neighbour is valid. No attempts to check the entry +will be made. +\item\verb|permanent| --- it is a \verb|noarp| entry, but only the administrator +may remove the entry from the neighbour table. +\end{enumerate} + +The link layer address is valid in all states except for \verb|none|, +\verb|failed| and \verb|incomplete|. + +IPv6 neighbours can be marked with the additional flag \verb|router| +which means that the neighbour introduced itself as an IPv6 router~\cite{RFC-NDISC}. + +\paragraph{Statistics:} The \verb|-statistics| option displays some usage +statistics, f.e.\ + +\begin{verbatim} +kuznet@alisa:~ $ ip -s n ls 193.233.7.254 +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \ + nud reachable +kuznet@alisa:~ $ +\end{verbatim} + +Here \verb|ref| is the number of users of this entry +and \verb|used| is a triplet of time intervals in seconds +separated by slashes. In this case they show that: + +\begin{enumerate} +\item the entry was used 12 seconds ago. +\item the entry was confirmed 13 seconds ago. +\item the entry was updated 20 seconds ago. +\end{enumerate} + +\subsection{{\tt ip neighbour flush} --- flush neighbour entries} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:}This command flushes neighbour tables, selecting +entries to flush by some criteria. + +\paragraph{Arguments:} This command has the same arguments as \verb|show|. +The differences are that it does not run when no arguments are given, +and that the default neighbour states to be flushed do not include +\verb|permanent| and \verb|noarp|. + + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted neighbours and the number +of rounds made to flush the neighbour table. If the option is given +twice, \verb|ip neigh flush| also dumps all the deleted neighbours +in the format described in the previous subsection. + +\paragraph{Example:} +\begin{verbatim} +netadm@alisa:~ # ip -s -s n f 193.233.7.254 +193.233.7.254 dev eth0 lladdr 00:00:0c:76:3f:85 ref 5 used 12/13/20 \ + nud reachable + +*** Round 1, deleting 1 entries *** +*** Flush is complete after 1 round *** +netadm@alisa:~ # +\end{verbatim} + + +\section{{\tt ip route} --- routing table management} +\label{IP-ROUTE} + +\paragraph{Abbreviations:} \verb|route|, \verb|ro|, \verb|r|. + +\paragraph{Object:} \verb|route| entries in the kernel routing tables keep +information about paths to other networked nodes. + +Each route entry has a {\em key\/} consisting of a {\em prefix\/} +(i.e.\ a pair containing a network address and the length of its mask) and, +optionally, the TOS value. An IP packet matches the route if the highest +bits of its destination address are equal to the route prefix at least +up to the prefix length and if the TOS of the route is zero or equal to +the TOS of the packet. + +If several routes match the packet, the following pruning rules +are used to select the best one (see~\cite{RFC1812}): +\begin{enumerate} +\item The longest matching prefix is selected. All shorter ones +are dropped. + +\item If the TOS of some route with the longest prefix is equal to the TOS +of the packet, the routes with different TOS are dropped. + +If no exact TOS match was found and routes with TOS=0 exist, +the rest of routes are pruned. + +Otherwise, the route lookup fails. + +\item If several routes remain after the previous steps, then +the routes with the best preference values are selected. + +\item If we still have several routes, then the {\em first\/} of them +is selected. + +\begin{NB} + Note the ambiguity of the last step. Unfortunately, Linux + historically allows such a bizarre situation. The sense of the +word ``first'' depends on the order of route additions and it is practically +impossible to maintain a bundle of such routes in this order. +\end{NB} + +For simplicity we will limit ourselves to the case where such a situation +is impossible and routes are uniquely identified by the triplet +\{prefix, tos, preference\}. Actually, it is impossible to create +non-unique routes with \verb|ip| commands described in this section. + +One useful exception to this rule is the default route on non-forwarding +hosts. It is ``officially'' allowed to have several fallback routes +when several routers are present on directly connected networks. +In this case, Linux-2.2 makes ``dead gateway detection''~\cite{RFC1122} +controlled by neighbour unreachability detection and by advice +from transport protocols to select a working router, so the order +of the routes is not essential. However, in this case, +fiddling with default routes manually is not recommended. Use the Router Discovery +protocol (see Appendix~\ref{EXAMPLE-SETUP}, p.\pageref{EXAMPLE-SETUP}) +instead. Actually, Linux-2.2 IPv6 does not give user level applications +any access to default routes. +\end{enumerate} + +Certainly, the steps above are not performed exactly +in this sequence. Instead, the routing table in the kernel is kept +in some data structure to achieve the final result +with minimal cost. However, not depending on a particular +routing algorithm implemented in the kernel, we can summarize +the statements above as: a route is identified by the triplet +\{prefix, tos, preference\}. This {\em key\/} lets us locate +the route in the routing table. + +\paragraph{Route attributes:} Each route key refers to a routing +information record containing +the data required to deliver IP packets (f.e.\ output device and +next hop router) and some optional attributes (f.e. the path MTU or +the preferred source address when communicating with this destination). +These attributes are described in the following subsection. + +\paragraph{Route types:} \label{IP-ROUTE-TYPES} +It is important that the set +of required and optional attributes depend on the route {\em type\/}. +The most important route type +is \verb|unicast|. It describes real paths to other hosts. +As a rule, common routing tables contain only such routes. However, +there are other types of routes with different semantics. The +full list of types understood by Linux-2.2 is: +\begin{itemize} +\item \verb|unicast| --- the route entry describes real paths to the +destinations covered by the route prefix. +\item \verb|unreachable| --- these destinations are unreachable. Packets +are discarded and the ICMP message {\em host unreachable\/} is generated. +The local senders get an \verb|EHOSTUNREACH| error. +\item \verb|blackhole| --- these destinations are unreachable. Packets +are discarded silently. The local senders get an \verb|EINVAL| error. +\item \verb|prohibit| --- these destinations are unreachable. Packets +are discarded and the ICMP message {\em communication administratively +prohibited\/} is generated. The local senders get an \verb|EACCES| error. +\item \verb|local| --- the destinations are assigned to this +host. The packets are looped back and delivered locally. +\item \verb|broadcast| --- the destinations are broadcast addresses. +The packets are sent as link broadcasts. +\item \verb|throw| --- a special control route used together with policy +rules (see sec.\ref{IP-RULE}, p.\pageref{IP-RULE}). If such a route is selected, lookup +in this table is terminated pretending that no route was found. +Without policy routing it is equivalent to the absence of the route in the routing +table. The packets are dropped and the ICMP message {\em net unreachable\/} +is generated. The local senders get an \verb|ENETUNREACH| error. +\item \verb|nat| --- a special NAT route. Destinations covered by the prefix +are considered to be dummy (or external) addresses which require translation +to real (or internal) ones before forwarding. The addresses to translate to +are selected with the attribute \verb|via|. More about NAT is +in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}. +\item \verb|anycast| --- ({\em not implemented\/}) the destinations are +{\em anycast\/} addresses assigned to this host. They are mainly equivalent +to \verb|local| with one difference: such addresses are invalid when used +as the source address of any packet. +\item \verb|multicast| --- a special type used for multicast routing. +It is not present in normal routing tables. +\end{itemize} + +\paragraph{Route tables:} Linux-2.2 can pack routes into several routing +tables identified by a number in the range from 1 to 255 or by +name from the file \verb|/etc/iproute2/rt_tables|. By default all normal +routes are inserted into the \verb|main| table (ID 254) and the kernel only uses +this table when calculating routes. + +Actually, one other table always exists, which is invisible but +even more important. It is the \verb|local| table (ID 255). This table +consists of routes for local and broadcast addresses. The kernel maintains +this table automatically and the administrator usually need not modify it +or even look at it. + +The multiple routing tables enter the game when {\em policy routing\/} +is used. See sec.\ref{IP-RULE}, p.\pageref{IP-RULE}. +In this case, the table identifier effectively becomes +one more parameter, which should be added to the triplet +\{prefix, tos, preference\} to uniquely identify the route. + + +\subsection{{\tt ip route add} --- add a new route\\ + {\tt ip route change} --- change a route\\ + {\tt ip route replace} --- change a route or add a new one} +\label{IP-ROUTE-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; + \verb|replace|, \verb|repl|. + + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to PREFIX| or \verb|to TYPE PREFIX| (default) + +--- the destination prefix of the route. If \verb|TYPE| is omitted, +\verb|ip| assumes type \verb|unicast|. Other values of \verb|TYPE| +are listed above. \verb|PREFIX| is an IP or IPv6 address optionally followed +by a slash and the prefix length. If the length of the prefix is missing, +\verb|ip| assumes a full-length host route. There is also a special +\verb|PREFIX| --- \verb|default| --- which is equivalent to IP \verb|0/0| or +to IPv6 \verb|::/0|. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- the Type Of Service (TOS) key. This key has no associated mask and +the longest match is understood as: First, compare the TOS +of the route and of the packet. If they are not equal, then the packet +may still match a route with a zero TOS. \verb|TOS| is either an 8 bit hexadecimal +number or an identifier from {\tt /etc/iproute2/rt\_dsfield}. + + +\item \verb|metric NUMBER| or \verb|preference NUMBER| + +--- the preference value of the route. \verb|NUMBER| is an arbitrary 32bit number. + +\item \verb|table TABLEID| + +--- the table to add this route to. +\verb|TABLEID| may be a number or a string from the file +\verb|/etc/iproute2/rt_tables|. If this parameter is omitted, +\verb|ip| assumes the \verb|main| table, with the exception of +\verb|local|, \verb|broadcast| and \verb|nat| routes, which are +put into the \verb|local| table by default. + +\item \verb|dev NAME| + +--- the output device name. + +\item \verb|via ADDRESS| + +--- the address of the nexthop router. Actually, the sense of this field depends +on the route type. For normal \verb|unicast| routes it is either the true nexthop +router or, if it is a direct route installed in BSD compatibility mode, +it can be a local address of the interface. +For NAT routes it is the first address of the block of translated IP destinations. + +\item \verb|src ADDRESS| + +--- the source address to prefer when sending to the destinations +covered by the route prefix. + +\item \verb|realm REALMID| + +--- the realm to which this route is assigned. +\verb|REALMID| may be a number or a string from the file +\verb|/etc/iproute2/rt_realms|. Sec.\ref{RT-REALMS} (p.\pageref{RT-REALMS}) +contains more information on realms. + +\item \verb|mtu MTU| or \verb|mtu lock MTU| + +--- the MTU along the path to the destination. If the modifier \verb|lock| is +not used, the MTU may be updated by the kernel due to Path MTU Discovery. +If the modifier \verb|lock| is used, no path MTU discovery will be tried, +all packets will be sent without the DF bit in IPv4 case +or fragmented to MTU for IPv6. + +\item \verb|window NUMBER| + +--- the maximal window for TCP to advertise to these destinations, +measured in bytes. It limits maximal data bursts that our TCP +peers are allowed to send to us. + +\item \verb|rtt NUMBER| + +--- the initial RTT (``Round Trip Time'') estimate. + + +\item \verb|rttvar NUMBER| + +--- \threeonly the initial RTT variance estimate. + + +\item \verb|ssthresh NUMBER| + +--- \threeonly an estimate for the initial slow start threshold. + + +\item \verb|cwnd NUMBER| + +--- \threeonly the clamp for congestion window. It is ignored if the \verb|lock| + flag is not used. + + +\item \verb|advmss NUMBER| + +--- \threeonly the MSS (``Maximal Segment Size'') to advertise to these + destinations when establishing TCP connections. If it is not given, + Linux uses a default value calculated from the first hop device MTU. + +\begin{NB} + If the path to these destination is asymmetric, this guess may be wrong. +\end{NB} + +\item \verb|reordering NUMBER| + +--- \threeonly Maximal reordering on the path to this destination. + If it is not given, Linux uses the value selected with \verb|sysctl| + variable \verb|net/ipv4/tcp_reordering|. + + + +\item \verb|nexthop NEXTHOP| + +--- the nexthop of a multipath route. \verb|NEXTHOP| is a complex value +with its own syntax similar to the top level argument lists: +\begin{itemize} +\item \verb|via ADDRESS| is the nexthop router. +\item \verb|dev NAME| is the output device. +\item \verb|weight NUMBER| is a weight for this element of a multipath +route reflecting its relative bandwidth or quality. +\end{itemize} + +\item \verb|scope SCOPE_VAL| + +--- the scope of the destinations covered by the route prefix. +\verb|SCOPE_VAL| may be a number or a string from the file +\verb|/etc/iproute2/rt_scopes|. +If this parameter is omitted, +\verb|ip| assumes scope \verb|global| for all gatewayed \verb|unicast| +routes, scope \verb|link| for direct \verb|unicast| and \verb|broadcast| routes +and scope \verb|host| for \verb|local| routes. + +\item \verb|protocol RTPROTO| + +--- the routing protocol identifier of this route. +\verb|RTPROTO| may be a number or a string from the file +\verb|/etc/iproute2/rt_protos|. If the routing protocol ID is +not given, \verb|ip| assumes protocol \verb|boot| (i.e.\ +it assumes the route was added by someone who doesn't +understand what they are doing). Several protocol values have a fixed interpretation. +Namely: +\begin{itemize} +\item \verb|redirect| --- the route was installed due to an ICMP redirect. +\item \verb|kernel| --- the route was installed by the kernel during +autoconfiguration. +\item \verb|boot| --- the route was installed during the bootup sequence. +If a routing daemon starts, it will purge all of them. +\item \verb|static| --- the route was installed by the administrator +to override dynamic routing. Routing daemon will respect them +and, probably, even advertise them to its peers. +\item \verb|ra| --- the route was installed by Router Discovery protocol. +\end{itemize} +The rest of the values are not reserved and the administrator is free +to assign (or not to assign) protocol tags. At least, routing +daemons should take care of setting some unique protocol values, +f.e.\ as they are assigned in \verb|rtnetlink.h| or in \verb|rt_protos| +database. + + +\item \verb|onlink| + +--- pretend that the nexthop is directly attached to this link, +even if it does not match any interface prefix. One application of this +option may be found in~\cite{IP-TUNNELS}. + +\item \verb|equalize| + +--- allow packet by packet randomization on multipath routes. +Without this modifier, the route will be frozen to one selected +nexthop, so that load splitting will only occur on per-flow base. +\verb|equalize| only works if the kernel is patched. + + +\end{itemize} + + +\begin{NB} + Actually there are more commands: \verb|prepend| does the same + thing as classic \verb|route add|, i.e.\ adds a route, even if another + route to the same destination exists. Its opposite case is \verb|append|, + which adds the route to the end of the list. Avoid these + features. +\end{NB} +\begin{NB} + More sad news, IPv6 only understands the \verb|append| command correctly. + All the others are translated into \verb|append| commands. Certainly, + this will change in the future. +\end{NB} + +\paragraph{Examples:} +\begin{itemize} +\item add a plain route to network 10.0.0/24 via gateway 193.233.7.65 +\begin{verbatim} + ip route add 10.0.0/24 via 193.233.7.65 +\end{verbatim} +\item change it to a direct route via the \verb|dummy| device +\begin{verbatim} + ip ro chg 10.0.0/24 dev dummy +\end{verbatim} +\item add a default multipath route splitting the load between \verb|ppp0| +and \verb|ppp1| +\begin{verbatim} + ip route add default scope global nexthop dev ppp0 \ + nexthop dev ppp1 +\end{verbatim} +Note the scope value. It is not necessary but it informs the kernel +that this route is gatewayed rather than direct. Actually, if you +know the addresses of remote endpoints it would be better to use the +\verb|via| parameter. +\item announce that the address 192.203.80.144 is not a real one, but +should be translated to 193.233.7.83 before forwarding +\begin{verbatim} + ip route add nat 192.203.80.144 via 193.233.7.83 +\end{verbatim} +Backward translation is setup with policy rules described +in the following section (sec.\ref{IP-RULE}, p.\pageref{IP-RULE}). +\end{itemize} + +\subsection{{\tt ip route delete} --- delete a route} + +\paragraph{Abbreviations:} \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Arguments:} \verb|ip route del| has the same arguments as +\verb|ip route add|, but their semantics are a bit different. + +Key values (\verb|to|, \verb|tos|, \verb|preference| and \verb|table|) +select the route to delete. If optional attributes are present, \verb|ip| +verifies that they coincide with the attributes of the route to delete. +If no route with the given key and attributes was found, \verb|ip route del| +fails. +\begin{NB} +Linux-2.0 had the option to delete a route selected only by prefix address, +ignoring its length (i.e.\ netmask). This option no longer exists +because it was ambiguous. However, look at {\tt ip route flush} +(sec.\ref{IP-ROUTE-FLUSH}, p.\pageref{IP-ROUTE-FLUSH}) which +provides similar and even richer functionality. +\end{NB} + +\paragraph{Example:} +\begin{itemize} +\item delete the multipath route created by the command in previous subsection +\begin{verbatim} + ip route del default scope global nexthop dev ppp0 \ + nexthop dev ppp1 +\end{verbatim} +\end{itemize} + + + +\subsection{{\tt ip route show} --- list routes} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Description:} the command displays the contents of the routing tables +or the route(s) selected by some criteria. + + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to SELECTOR| (default) + +--- only select routes from the given range of destinations. \verb|SELECTOR| +consists of an optional modifier (\verb|root|, \verb|match| or \verb|exact|) +and a prefix. \verb|root PREFIX| selects routes with prefixes not shorter +than \verb|PREFIX|. F.e.\ \verb|root 0/0| selects the entire routing table. +\verb|match PREFIX| selects routes with prefixes not longer than +\verb|PREFIX|. F.e.\ \verb|match 10.0/16| selects \verb|10.0/16|, +\verb|10/8| and \verb|0/0|, but it does not select \verb|10.1/16| and +\verb|10.0.0/24|. And \verb|exact PREFIX| (or just \verb|PREFIX|) +selects routes with this exact prefix. If neither of these options +are present, \verb|ip| assumes \verb|root 0/0| i.e.\ it lists the entire table. + + +\item \verb|tos TOS| or \verb|dsfield TOS| + + --- only select routes with the given TOS. + + +\item \verb|table TABLEID| + + --- show the routes from this table(s). The default setting is to show +\verb|table| \verb|main|. \verb|TABLEID| may either be the ID of a real table +or one of the special values: + \begin{itemize} + \item \verb|all| --- list all of the tables. + \item \verb|cache| --- dump the routing cache. + \end{itemize} +\begin{NB} + IPv6 has a single table. However, splitting it into \verb|main|, \verb|local| + and \verb|cache| is emulated by the \verb|ip| utility. +\end{NB} + +\item \verb|cloned| or \verb|cached| + +--- list cloned routes i.e.\ routes which were dynamically forked from +other routes because some route attribute (f.e.\ MTU) was updated. +Actually, it is equivalent to \verb|table cache|. + +\item \verb|from SELECTOR| + +--- the same syntax as for \verb|to|, but it binds the source address range +rather than destinations. Note that the \verb|from| option only works with +cloned routes. + +\item \verb|protocol RTPROTO| + +--- only list routes of this protocol. + + +\item \verb|scope SCOPE_VAL| + +--- only list routes with this scope. + +\item \verb|type TYPE| + +--- only list routes of this type. + +\item \verb|dev NAME| + +--- only list routes going via this device. + +\item \verb|via PREFIX| + +--- only list routes going via the nexthop routers selected by \verb|PREFIX|. + +\item \verb|src PREFIX| + +--- only list routes with preferred source addresses selected +by \verb|PREFIX|. + +\item \verb|realm REALMID| or \verb|realms FROMREALM/TOREALM| + +--- only list routes with these realms. + +\end{itemize} + +\paragraph{Examples:} Let us count routes of protocol \verb|gated/bgp| +on a router: +\begin{verbatim} +kuznet@amber:~ $ ip ro ls proto gated/bgp | wc + 1413 9891 79010 +kuznet@amber:~ $ +\end{verbatim} +To count the size of the routing cache, we have to use the \verb|-o| option +because cached attributes can take more than one line of output: +\begin{verbatim} +kuznet@amber:~ $ ip -o ro ls cloned | wc + 159 2543 18707 +kuznet@amber:~ $ +\end{verbatim} + + +\paragraph{Output format:} The output of this command consists +of per route records separated by line feeds. +However, some records may consist +of more than one line: particularly, this is the case when the route +is cloned or you requested additional statistics. If the +\verb|-o| option was given, then line feeds separating lines inside +records are replaced with the backslash sign. + +The output has the same syntax as arguments given to {\tt ip route add}, +so that it can be understood easily. F.e.\ +\begin{verbatim} +kuznet@amber:~ $ ip ro ls 193.233.7/24 +193.233.7.0/24 dev eth0 proto gated/conn scope link \ + src 193.233.7.65 realms inr.ac +kuznet@amber:~ $ +\end{verbatim} + +If you list cloned entries, the output contains other attributes which +are evaluated during route calculation and updated during route +lifetime. An example of the output is: +\begin{verbatim} +kuznet@amber:~ $ ip ro ls 193.233.7.82 tab cache +193.233.7.82 from 193.233.7.82 dev eth0 src 193.233.7.65 \ + realms inr.ac/inr.ac + cache <src-direct,redirect> mtu 1500 rtt 300 iif eth0 +193.233.7.82 dev eth0 src 193.233.7.65 realms inr.ac + cache mtu 1500 rtt 300 +kuznet@amber:~ $ +\end{verbatim} +\begin{NB} + \label{NB-strange-route} + The route looks a bit strange, doesn't it? Did you notice that + it is a path from 193.233.7.82 back to 193.233.82? Well, you will + see in the section on \verb|ip route get| (p.\pageref{NB-nature-of-strangeness}) + how it appeared. +\end{NB} +The second line, starting with the word \verb|cache|, shows +additional attributes which normal routes do not possess. +Cached flags are summarized in angle brackets: +\begin{itemize} +\item \verb|local| --- packets are delivered locally. +It stands for loopback unicast routes, for broadcast routes +and for multicast routes, if this host is a member of the corresponding +group. + +\item \verb|reject| --- the path is bad. Any attempt to use it results +in an error. See attribute \verb|error| below (p.\pageref{IP-ROUTE-GET-error}). + +\item \verb|mc| --- the destination is multicast. + +\item \verb|brd| --- the destination is broadcast. + +\item \verb|src-direct| --- the source is on a directly connected +interface. + +\item \verb|redirected| --- the route was created by an ICMP Redirect. + +\item \verb|redirect| --- packets going via this route will +trigger an ICMP redirect. + +\item \verb|fastroute| --- the route is eligible to be used for fastroute. + +\item \verb|equalize| --- make packet by packet randomization +along this path. + +\item \verb|dst-nat| --- the destination address requires translation. + +\item \verb|src-nat| --- the source address requires translation. + +\item \verb|masq| --- the source address requires masquerading. +This feature disappeared in linux-2.4. + +\item \verb|notify| --- ({\em not implemented}) change/deletion +of this route will trigger RTNETLINK notification. +\end{itemize} + +Then some optional attributes follow: +\begin{itemize} +\item \verb|error| --- on \verb|reject| routes it is error code +returned to local senders when they try to use this route. +These error codes are translated into ICMP error codes, sent to remote +senders, according to the rules described above in the subsection +devoted to route types (p.\pageref{IP-ROUTE-TYPES}). +\label{IP-ROUTE-GET-error} + +\item \verb|expires| --- this entry will expire after this timeout. + +\item \verb|iif| --- the packets for this path are expected to arrive +on this interface. +\end{itemize} + +\paragraph{Statistics:} With the \verb|-statistics| option, more +information about this route is shown: +\begin{itemize} +\item \verb|users| --- the number of users of this entry. +\item \verb|age| --- shows when this route was last used. +\item \verb|used| --- the number of lookups of this route since its creation. +\end{itemize} + + +\subsection{{\tt ip route flush} --- flush routing tables} +\label{IP-ROUTE-FLUSH} + +\paragraph{Abbreviations:} \verb|flush|, \verb|f|. + +\paragraph{Description:} this command flushes routes selected +by some criteria. + +\paragraph{Arguments:} the arguments have the same syntax and semantics +as the arguments of \verb|ip route show|, but routing tables are not +listed but purged. The only difference is the default action: \verb|show| +dumps all the IP main routing table but \verb|flush| prints the helper page. +The reason for this difference does not require any explanation, does it? + + +\paragraph{Statistics:} With the \verb|-statistics| option, the command +becomes verbose. It prints out the number of deleted routes and the number +of rounds made to flush the routing table. If the option is given +twice, \verb|ip route flush| also dumps all the deleted routes +in the format described in the previous subsection. + +\paragraph{Examples:} The first example flushes all the +gatewayed routes from the main table (f.e.\ after a routing daemon crash). +\begin{verbatim} +netadm@amber:~ # ip -4 ro flush scope global type unicast +\end{verbatim} +This option deserves to be put into a scriptlet \verb|routef|. +\begin{NB} +This option was described in the \verb|route(8)| man page borrowed +from BSD, but was never implemented in Linux. +\end{NB} + +The second example flushes all IPv6 cloned routes: +\begin{verbatim} +netadm@amber:~ # ip -6 -s -s ro flush cache +3ffe:2400::220:afff:fef4:c5d1 via 3ffe:2400::220:afff:fef4:c5d1 \ + dev eth0 metric 0 + cache used 2 age 12sec mtu 1500 rtt 300 +3ffe:2400::280:adff:feb7:8034 via 3ffe:2400::280:adff:feb7:8034 \ + dev eth0 metric 0 + cache used 2 age 15sec mtu 1500 rtt 300 +3ffe:2400::280:c8ff:fe59:5bcc via 3ffe:2400::280:c8ff:fe59:5bcc \ + dev eth0 metric 0 + cache users 1 used 1 age 23sec mtu 1500 rtt 300 +3ffe:2400:0:1:2a0:ccff:fe66:1878 via 3ffe:2400:0:1:2a0:ccff:fe66:1878 \ + dev eth1 metric 0 + cache used 2 age 20sec mtu 1500 rtt 300 +3ffe:2400:0:1:a00:20ff:fe71:fb30 via 3ffe:2400:0:1:a00:20ff:fe71:fb30 \ + dev eth1 metric 0 + cache used 2 age 33sec mtu 1500 rtt 300 +ff02::1 via ff02::1 dev eth1 metric 0 + cache users 1 used 1 age 45sec mtu 1500 rtt 300 + +*** Round 1, deleting 6 entries *** +*** Flush is complete after 1 round *** +netadm@amber:~ # ip -6 -s -s ro flush cache +Nothing to flush. +netadm@amber:~ # +\end{verbatim} + +The third example flushes BGP routing tables after a \verb|gated| +death. +\begin{verbatim} +netadm@amber:~ # ip ro ls proto gated/bgp | wc + 1408 9856 78730 +netadm@amber:~ # ip -s ro f proto gated/bgp + +*** Round 1, deleting 1408 entries *** +*** Flush is complete after 1 round *** +netadm@amber:~ # ip ro f proto gated/bgp +Nothing to flush. +netadm@amber:~ # ip ro ls proto gated/bgp +netadm@amber:~ # +\end{verbatim} + + +\subsection{{\tt ip route get} --- get a single route} +\label{IP-ROUTE-GET} + +\paragraph{Abbreviations:} \verb|get|, \verb|g|. + +\paragraph{Description:} this command gets a single route to a destination +and prints its contents exactly as the kernel sees it. + +\paragraph{Arguments:} +\begin{itemize} +\item \verb|to ADDRESS| (default) + +--- the destination address. + +\item \verb|from ADDRESS| + +--- the source address. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- the Type Of Service. + +\item \verb|iif NAME| + +--- the device from which this packet is expected to arrive. + +\item \verb|oif NAME| + +--- force the output device on which this packet will be routed. + +\item \verb|connected| + +--- if no source address (option \verb|from|) was given, relookup +the route with the source set to the preferred address received from the first lookup. +If policy routing is used, it may be a different route. + +\end{itemize} + +Note that this operation is not equivalent to \verb|ip route show|. +\verb|show| shows existing routes. \verb|get| resolves them and +creates new clones if necessary. Essentially, \verb|get| +is equivalent to sending a packet along this path. +If the \verb|iif| argument is not given, the kernel creates a route +to output packets towards the requested destination. +This is equivalent to pinging the destination +with a subsequent {\tt ip route ls cache}, however, no packets are +actually sent. With the \verb|iif| argument, the kernel pretends +that a packet arrived from this interface and searches for +a path to forward the packet. + +\paragraph{Output format:} This command outputs routes in the same +format as \verb|ip route ls|. + +\paragraph{Examples:} +\begin{itemize} +\item Find a route to output packets to 193.233.7.82: +\begin{verbatim} +kuznet@amber:~ $ ip route get 193.233.7.82 +193.233.7.82 dev eth0 src 193.233.7.65 realms inr.ac + cache mtu 1500 rtt 300 +kuznet@amber:~ $ +\end{verbatim} + +\item Find a route to forward packets arriving on \verb|eth0| +from 193.233.7.82 and destined for 193.233.7.82: +\begin{verbatim} +kuznet@amber:~ $ ip r g 193.233.7.82 from 193.233.7.82 iif eth0 +193.233.7.82 from 193.233.7.82 dev eth0 src 193.233.7.65 \ + realms inr.ac/inr.ac + cache <src-direct,redirect> mtu 1500 rtt 300 iif eth0 +kuznet@amber:~ $ +\end{verbatim} +\begin{NB} + \label{NB-nature-of-strangeness} + This is the command that created the funny route from 193.233.7.82 + looped back to 193.233.7.82 (cf.\ NB on~p.\pageref{NB-strange-route}). + Note the \verb|redirect| flag on it. +\end{NB} + +\item Find a multicast route for packets arriving on \verb|eth0| +from host 193.233.7.82 and destined for multicast group 224.2.127.254 +(it is assumed that a multicast routing daemon is running. +In this case, it is \verb|pimd|) +\begin{verbatim} +kuznet@amber:~ $ ip r g 224.2.127.254 from 193.233.7.82 iif eth0 +multicast 224.2.127.254 from 193.233.7.82 dev lo \ + src 193.233.7.65 realms inr.ac/cosmos + cache <mc> iif eth0 Oifs: eth1 pimreg +kuznet@amber:~ $ +\end{verbatim} +This route differs from the ones seen before. It contains a ``normal'' part +and a ``multicast'' part. The normal part is used to deliver (or not to +deliver) the packet to local IP listeners. In this case the router +is not a member +of this group, so that route has no \verb|local| flag and only +forwards packets. The output device for such entries is always loopback. +The multicast part consists of an additional \verb|Oifs:| list showing +the output interfaces. +\end{itemize} + + +It is time for a more complicated example. Let us add an invalid +gatewayed route for a destination which is really directly connected: +\begin{verbatim} +netadm@alisa:~ # ip route add 193.233.7.98 via 193.233.7.254 +netadm@alisa:~ # ip route get 193.233.7.98 +193.233.7.98 via 193.233.7.254 dev eth0 src 193.233.7.90 + cache mtu 1500 rtt 3072 +netadm@alisa:~ # +\end{verbatim} +and probe it with ping: +\begin{verbatim} +netadm@alisa:~ # ping -n 193.233.7.98 +PING 193.233.7.98 (193.233.7.98) from 193.233.7.90 : 56 data bytes +From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98) +64 bytes from 193.233.7.98: icmp_seq=0 ttl=255 time=3.5 ms +From 193.233.7.254: Redirect Host(New nexthop: 193.233.7.98) +64 bytes from 193.233.7.98: icmp_seq=1 ttl=255 time=2.2 ms +64 bytes from 193.233.7.98: icmp_seq=2 ttl=255 time=0.4 ms +64 bytes from 193.233.7.98: icmp_seq=3 ttl=255 time=0.4 ms +64 bytes from 193.233.7.98: icmp_seq=4 ttl=255 time=0.4 ms +^C +--- 193.233.7.98 ping statistics --- +5 packets transmitted, 5 packets received, 0% packet loss +round-trip min/avg/max = 0.4/1.3/3.5 ms +netadm@alisa:~ # +\end{verbatim} +What happened? Router 193.233.7.254 understood that we have a much +better path to the destination and sent us an ICMP redirect message. +We may retry \verb|ip route get| to see what we have in the routing +tables now: +\begin{verbatim} +netadm@alisa:~ # ip route get 193.233.7.98 +193.233.7.98 dev eth0 src 193.233.7.90 + cache <redirected> mtu 1500 rtt 3072 +netadm@alisa:~ # +\end{verbatim} + + + +\section{{\tt ip rule} --- routing policy database management} +\label{IP-RULE} + +\paragraph{Abbreviations:} \verb|rule|, \verb|ru|. + +\paragraph{Object:} \verb|rule|s in the routing policy database control +the route selection algorithm. + +Classic routing algorithms used in the Internet make routing decisions +based only on the destination address of packets (and in theory, +but not in practice, on the TOS field). The seminal review of classic +routing algorithms and their modifications can be found in~\cite{RFC1812}. + +In some circumstances we want to route packets differently depending not only +on destination addresses, but also on other packet fields: source address, +IP protocol, transport protocol ports or even packet payload. +This task is called ``policy routing''. + +\begin{NB} + ``policy routing'' $\neq$ ``routing policy''. + +\noindent ``policy routing'' $=$ ``cunning routing''. + +\noindent ``routing policy'' $=$ ``routing tactics'' or ``routing plan''. +\end{NB} + +To solve this task, the conventional destination based routing table, ordered +according to the longest match rule, is replaced with a ``routing policy +database'' (or RPDB), which selects routes +by executing some set of rules. The rules may have lots of keys of different +natures and therefore they have no natural ordering, but one imposed +by the administrator. Linux-2.2 RPDB is a linear list of rules +ordered by numeric priority value. +RPDB explicitly allows matching a few packet fields: + +\begin{itemize} +\item packet source address. +\item packet destination address. +\item TOS. +\item incoming interface (which is packet metadata, rather than a packet field). +\end{itemize} + +Matching IP protocols and transport ports is also possible, +indirectly, via \verb|ipchains|, by exploiting their ability +to mark some classes of packets with \verb|fwmark|. Therefore, +\verb|fwmark| is also included in the set of keys checked by rules. + +Each policy routing rule consists of a {\em selector\/} and an {\em action\/} +predicate. The RPDB is scanned in the order of increasing priority. The selector +of each rule is applied to \{source address, destination address, incoming +interface, tos, fwmark\} and, if the selector matches the packet, +the action is performed. The action predicate may return with success. +In this case, it will either give a route or failure indication +and the RPDB lookup is terminated. Otherwise, the RPDB program +continues on the next rule. + +What is the action, semantically? The natural action is to select the +nexthop and the output device. This is what +Cisco IOS~\cite{IOS} does. Let us call it ``match \& set''. +The Linux-2.2 approach is more flexible. The action includes +lookups in destination-based routing tables and selecting +a route from these tables according to the classic longest match algorithm. +The ``match \& set'' approach is the simplest case of the Linux one. It is realized +when a second level routing table contains a single default route. +Recall that Linux-2.2 supports multiple tables +managed with the \verb|ip route| command, described in the previous section. + +At startup time the kernel configures the default RPDB consisting of three +rules: + +\begin{enumerate} +\item Priority: 0, Selector: match anything, Action: lookup routing +table \verb|local| (ID 255). +The \verb|local| table is a special routing table containing +high priority control routes for local and broadcast addresses. + +Rule 0 is special. It cannot be deleted or overridden. + + +\item Priority: 32766, Selector: match anything, Action: lookup routing +table \verb|main| (ID 254). +The \verb|main| table is the normal routing table containing all non-policy +routes. This rule may be deleted and/or overridden with other +ones by the administrator. + +\item Priority: 32767, Selector: match anything, Action: lookup routing +table \verb|default| (ID 253). +The \verb|default| table is empty. It is reserved for some +post-processing if no previous default rules selected the packet. +This rule may also be deleted. + +\end{enumerate} + +Do not confuse routing tables with rules: rules point to routing tables, +several rules may refer to one routing table and some routing tables +may have no rules pointing to them. If the administrator deletes all the rules +referring to a table, the table is not used, but it still exists +and will disappear only after all the routes contained in it are deleted. + + +\paragraph{Rule attributes:} Each RPDB entry has additional +attributes. F.e.\ each rule has a pointer to some routing +table. NAT and masquerading rules have an attribute to select new IP +address to translate/masquerade. Besides that, rules have some +optional attributes, which routes have, namely \verb|realms|. +These values do not override those contained in the routing tables. They +are only used if the route did not select any attributes. + + +\paragraph{Rule types:} The RPDB may contain rules of the following +types: +\begin{itemize} +\item \verb|unicast| --- the rule prescribes to return the route found +in the routing table referenced by the rule. +\item \verb|blackhole| --- the rule prescribes to silently drop the packet. +\item \verb|unreachable| --- the rule prescribes to generate a ``Network +is unreachable'' error. +\item \verb|prohibit| --- the rule prescribes to generate +``Communication is administratively prohibited'' error. +\item \verb|nat| --- the rule prescribes to translate the source address +of the IP packet into some other value. More about NAT is +in Appendix~\ref{ROUTE-NAT}, p.\pageref{ROUTE-NAT}. +\end{itemize} + + +\paragraph{Commands:} \verb|add|, \verb|delete| and \verb|show| +(or \verb|list|). + +\subsection{{\tt ip rule add} --- insert a new rule\\ + {\tt ip rule delete} --- delete a rule} +\label{IP-RULE-ADD} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|, + \verb|d|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|type TYPE| (default) + +--- the type of this rule. The list of valid types was given in the previous +subsection. + +\item \verb|from PREFIX| + +--- select the source prefix to match. + +\item \verb|to PREFIX| + +--- select the destination prefix to match. + +\item \verb|iif NAME| + +--- select the incoming device to match. If the interface is loopback, +the rule only matches packets originating from this host. This means that you +may create separate routing tables for forwarded and local packets and, +hence, completely segregate them. + +\item \verb|tos TOS| or \verb|dsfield TOS| + +--- select the TOS value to match. + +\item \verb|fwmark MARK| + +--- select the \verb|fwmark| value to match. + +\item \verb|priority PREFERENCE| + +--- the priority of this rule. Each rule should have an explicitly +set {\em unique\/} priority value. +\begin{NB} + Really, for historical reasons \verb|ip rule add| does not require a + priority value and allows them to be non-unique. + If the user does not supplied a priority, it is selected by the kernel. + If the user creates a rule with a priority value that + already exists, the kernel does not reject the request. It adds + the new rule before all old rules of the same priority. + + It is mistake in design, no more. And it will be fixed one day, + so do not rely on this feature. Use explicit priorities. +\end{NB} + + +\item \verb|table TABLEID| + +--- the routing table identifier to lookup if the rule selector matches. + +\item \verb|realms FROM/TO| + +--- Realms to select if the rule matched and the routing table lookup +succeeded. Realm \verb|TO| is only used if the route did not select +any realm. + +\item \verb|nat ADDRESS| + +--- The base of the IP address block to translate (for source addresses). +The \verb|ADDRESS| may be either the start of the block of NAT addresses +(selected by NAT routes) or in linux-2.2 a local host address (or even zero). +In the last case the router does not translate the packets, +but masquerades them to this address; this feature disappered in 2.4. +More about NAT is in Appendix~\ref{ROUTE-NAT}, +p.\pageref{ROUTE-NAT}. + +\end{itemize} + +\paragraph{Warning:} Changes to the RPDB made with these commands +do not become active immediately. It is assumed that after +a script finishes a batch of updates, it flushes the routing cache +with \verb|ip route flush cache|. + +\paragraph{Examples:} +\begin{itemize} +\item Route packets with source addresses from 192.203.80/24 +according to routing table \verb|inr.ruhep|: +\begin{verbatim} +ip ru add from 192.203.80.0/24 table inr.ruhep prio 220 +\end{verbatim} + +\item Translate packet source address 193.233.7.83 into 192.203.80.144 +and route it according to table \#1 (actually, it is \verb|inr.ruhep|): +\begin{verbatim} +ip ru add from 193.233.7.83 nat 192.203.80.144 table 1 prio 320 +\end{verbatim} + +\item Delete the unused default rule: +\begin{verbatim} +ip ru del prio 32767 +\end{verbatim} + +\end{itemize} + + + +\subsection{{\tt ip rule show} --- list rules} +\label{IP-RULE-SHOW} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + + +\paragraph{Arguments:} Good news, this is one command that has no arguments. + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@amber:~ $ ip ru ls +0: from all lookup local +200: from 192.203.80.0/24 to 193.233.7.0/24 lookup main +210: from 192.203.80.0/24 to 192.203.80.0/24 lookup main +220: from 192.203.80.0/24 lookup inr.ruhep realms inr.ruhep/radio-msu +300: from 193.233.7.83 to 193.233.7.0/24 lookup main +310: from 193.233.7.83 to 192.203.80.0/24 lookup main +320: from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144 +32766: from all lookup main +kuznet@amber:~ $ +\end{verbatim} + +In the first column is the rule priority value followed +by a colon. Then the selectors follow. Each key is prefixed +with the same keyword that was used to create the rule. + +The keyword \verb|lookup| is followed by a routing table identifier, +as it is recorded in the file \verb|/etc/iproute2/rt_tables|. + +If the rule does NAT (f.e.\ rule \#320), it is shown by the keyword +\verb|map-to| followed by the start of the block of addresses to map. + +The sense of this example is pretty simple. The prefixes +192.203.80.0/24 and 193.233.7.0/24 form the internal network, but +they are routed differently when the packets leave it. +Besides that, the host 193.233.7.83 is translated into +another prefix to look like 192.203.80.144 when talking +to the outer world. + + + +\section{{\tt ip maddress} --- multicast addresses management} +\label{IP-MADDR} + +\paragraph{Object:} \verb|maddress| objects are multicast addresses. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|show| (or \verb|list|). + +\subsection{{\tt ip maddress show} --- list multicast addresses} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|dev NAME| (default) + +--- the device name. + +\end{itemize} + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@alisa:~ $ ip maddr ls dummy +2: dummy + link 33:33:00:00:00:01 + link 01:00:5e:00:00:01 + inet 224.0.0.1 users 2 + inet6 ff02::1 +kuznet@alisa:~ $ +\end{verbatim} + +The first line of the output shows the interface index and its name. +Then the multicast address list follows. Each line starts with the +protocol identifier. The word \verb|link| denotes a link layer +multicast addresses. + +If a multicast address has more than one user, the number +of users is shown after the \verb|users| keyword. + +One additional feature not present in the example above +is the \verb|static| flag, which indicates that the address was joined +with \verb|ip maddr add|. See the following subsection. + + + +\subsection{{\tt ip maddress add} --- add a multicast address\\ + {\tt ip maddress delete} --- delete a multicast address} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|delete|, \verb|del|, \verb|d|. + +\paragraph{Description:} these commands attach/detach +a static link layer multicast address to listen on the interface. +Note that it is impossible to join protocol multicast groups +statically. This command only manages link layer addresses. + + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|address LLADDRESS| (default) + +--- the link layer multicast address. + +\item \verb|dev NAME| + +--- the device to join/leave this multicast address. + +\end{itemize} + + +\paragraph{Example:} Let us continue with the example from the previous subsection. + +\begin{verbatim} +netadm@alisa:~ # ip maddr add 33:33:00:00:00:01 dev dummy +netadm@alisa:~ # ip -0 maddr ls dummy +2: dummy + link 33:33:00:00:00:01 users 2 static + link 01:00:5e:00:00:01 +netadm@alisa:~ # ip maddr del 33:33:00:00:00:01 dev dummy +\end{verbatim} + +\begin{NB} + Neither \verb|ip| nor the kernel check for multicast address validity. + Particularly, this means that you can try to load a unicast address + instead of a multicast address. Most drivers will ignore such addresses, + but several (f.e.\ Tulip) will intern it to their on-board filter. + The effects may be strange. Namely, the addresses become additional + local link addresses and, if you loaded the address of another host + to the router, wait for duplicated packets on the wire. + It is not a bug, but rather a hole in the API and intra-kernel interfaces. + This feature is really more useful for traffic monitoring, but using it + with Linux-2.2 you {\em have to\/} be sure that the host is not + a router and, especially, that it is not a transparent proxy or masquerading + agent. +\end{NB} + + + +\section{{\tt ip mroute} --- multicast routing cache management} +\label{IP-MROUTE} + +\paragraph{Abbreviations:} \verb|mroute|, \verb|mr|. + +\paragraph{Object:} \verb|mroute| objects are multicast routing cache +entries created by a user level mrouting daemon +(f.e.\ \verb|pimd| or \verb|mrouted|). + +Due to the limitations of the current interface to the multicast routing +engine, it is impossible to change \verb|mroute| objects administratively, +so we may only display them. This limitation will be removed +in the future. + +\paragraph{Commands:} \verb|show| (or \verb|list|). + + +\subsection{{\tt ip mroute show} --- list mroute cache entries} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + +\paragraph{Arguments:} + +\begin{itemize} +\item \verb|to PREFIX| (default) + +--- the prefix selecting the destination multicast addresses to list. + + +\item \verb|iif NAME| + +--- the interface on which multicast packets are received. + + +\item \verb|from PREFIX| + +--- the prefix selecting the IP source addresses of the multicast route. + + +\end{itemize} + +\paragraph{Output format:} + +\begin{verbatim} +kuznet@amber:~ $ ip mroute ls +(193.232.127.6, 224.0.1.39) Iif: unresolved +(193.232.244.34, 224.0.1.40) Iif: unresolved +(193.233.7.65, 224.66.66.66) Iif: eth0 Oifs: pimreg +kuznet@amber:~ $ +\end{verbatim} + +Each line shows one (S,G) entry in the multicast routing cache, +where S is the source address and G is the multicast group. \verb|Iif| is +the interface on which multicast packets are expected to arrive. +If the word \verb|unresolved| is there instead of the interface name, +it means that the routing daemon still hasn't resolved this entry. +The keyword \verb|oifs| is followed by a list of output interfaces, separated +by spaces. If a multicast routing entry is created with non-trivial +TTL scope, administrative distances are appended to the device names +in the \verb|oifs| list. + +\paragraph{Statistics:} The \verb|-statistics| option also prints the +number of packets and bytes forwarded along this route and +the number of packets that arrived on the wrong interface, if this number is not zero. + +\begin{verbatim} +kuznet@amber:~ $ ip -s mr ls 224.66/16 +(193.233.7.65, 224.66.66.66) Iif: eth0 Oifs: pimreg + 9383 packets, 300256 bytes +kuznet@amber:~ $ +\end{verbatim} + + +\section{{\tt ip tunnel} --- tunnel configuration} +\label{IP-TUNNEL} + +\paragraph{Abbreviations:} \verb|tunnel|, \verb|tunl|. + +\paragraph{Object:} \verb|tunnel| objects are tunnels, encapsulating +packets in IPv4 packets and then sending them over the IP infrastructure. + +\paragraph{Commands:} \verb|add|, \verb|delete|, \verb|change|, \verb|show| +(or \verb|list|). + +\paragraph{See also:} A more informal discussion of tunneling +over IP and the \verb|ip tunnel| command can be found in~\cite{IP-TUNNELS}. + +\subsection{{\tt ip tunnel add} --- add a new tunnel\\ + {\tt ip tunnel change} --- change an existing tunnel\\ + {\tt ip tunnel delete} --- destroy a tunnel} + +\paragraph{Abbreviations:} \verb|add|, \verb|a|; \verb|change|, \verb|chg|; +\verb|delete|, \verb|del|, \verb|d|. + + +\paragraph{Arguments:} + +\begin{itemize} + +\item \verb|name NAME| (default) + +--- select the tunnel device name. + +\item \verb|mode MODE| + +--- set the tunnel mode. Three modes are currently available: + \verb|ipip|, \verb|sit| and \verb|gre|. + +\item \verb|remote ADDRESS| + +--- set the remote endpoint of the tunnel. + +\item \verb|local ADDRESS| + +--- set the fixed local address for tunneled packets. +It must be an address on another interface of this host. + +\item \verb|ttl N| + +--- set a fixed TTL \verb|N| on tunneled packets. + \verb|N| is a number in the range 1--255. 0 is a special value + meaning that packets inherit the TTL value. + The default value is: \verb|inherit|. + +\item \verb|tos T| or \verb|dsfield T| + +--- set a fixed TOS \verb|T| on tunneled packets. + The default value is: \verb|inherit|. + + + +\item \verb|dev NAME| + +--- bind the tunnel to the device \verb|NAME| so that + tunneled packets will only be routed via this device and will + not be able to escape to another device when the route to endpoint changes. + +\item \verb|nopmtudisc| + +--- disable Path MTU Discovery on this tunnel. + It is enabled by default. Note that a fixed ttl is incompatible + with this option: tunnelling with a fixed ttl always makes pmtu discovery. + +\item \verb|key K|, \verb|ikey K|, \verb|okey K| + +--- (only GRE tunnels) use keyed GRE with key \verb|K|. \verb|K| is + either a number or an IP address-like dotted quad. + The \verb|key| parameter sets the key to use in both directions. + The \verb|ikey| and \verb|okey| parameters set different keys for input and output. + + +\item \verb|csum|, \verb|icsum|, \verb|ocsum| + +--- (only GRE tunnels) generate/require checksums for tunneled packets. + The \verb|ocsum| flag calculates checksums for outgoing packets. + The \verb|icsum| flag requires that all input packets have the correct + checksum. The \verb|csum| flag is equivalent to the combination + ``\verb|icsum| \verb|ocsum|''. + +\item \verb|seq|, \verb|iseq|, \verb|oseq| + +--- (only GRE tunnels) serialize packets. + The \verb|oseq| flag enables sequencing of outgoing packets. + The \verb|iseq| flag requires that all input packets are serialized. + The \verb|seq| flag is equivalent to the combination ``\verb|iseq| \verb|oseq|''. + +\begin{NB} + I think this option does not + work. At least, I did not test it, did not debug it and + do not even understand how it is supposed to work or for what + purpose Cisco planned to use it. Do not use it. +\end{NB} + + +\end{itemize} + +\paragraph{Example:} Create a pointopoint IPv6 tunnel with maximal TTL of 32. +\begin{verbatim} +netadm@amber:~ # ip tunl add Cisco mode sit remote 192.31.7.104 \ + local 192.203.80.142 ttl 32 +\end{verbatim} + +\subsection{{\tt ip tunnel show} --- list tunnels} + +\paragraph{Abbreviations:} \verb|show|, \verb|list|, \verb|sh|, \verb|ls|, \verb|l|. + + +\paragraph{Arguments:} None. + +\paragraph{Output format:} +\begin{verbatim} +kuznet@amber:~ $ ip tunl ls Cisco +Cisco: ipv6/ip remote 192.31.7.104 local 192.203.80.142 ttl 32 +kuznet@amber:~ $ +\end{verbatim} +The line starts with the tunnel device name followed by a colon. +Then the tunnel mode follows. The parameters of the tunnel are listed +with the same keywords that were used when creating the tunnel. + +\paragraph{Statistics:} + +\begin{verbatim} +kuznet@amber:~ $ ip -s tunl ls Cisco +Cisco: ipv6/ip remote 192.31.7.104 local 192.203.80.142 ttl 32 +RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts + 12566 1707516 0 0 0 0 +TX: Packets Bytes Errors DeadLoop NoRoute NoBufs + 13445 1879677 0 0 0 0 +kuznet@amber:~ $ +\end{verbatim} +Essentially, these numbers are the same as the numbers +printed with {\tt ip -s link show} +(sec.\ref{IP-LINK-SHOW}, p.\pageref{IP-LINK-SHOW}) but the tags are different +to reflect that they are tunnel specific. +\begin{itemize} +\item \verb|CsumErrs| --- the total number of packets dropped +because of checksum failures for a GRE tunnel with checksumming enabled. +\item \verb|OutOfSeq| --- the total number of packets dropped +because they arrived out of sequence for a GRE tunnel with +serialization enabled. +\item \verb|Mcasts| --- the total number of multicast packets +received on a broadcast GRE tunnel. +\item \verb|DeadLoop| --- the total number of packets which were not +transmitted because the tunnel is looped back to itself. +\item \verb|NoRoute| --- the total number of packets which were not +transmitted because there is no IP route to the remote endpoint. +\item \verb|NoBufs| --- the total number of packets which were not +transmitted because the kernel failed to allocate a buffer. +\end{itemize} + + +\section{{\tt ip monitor} and {\tt rtmon} --- state monitoring} +\label{IP-MONITOR} + +The \verb|ip| utility can monitor the state of devices, addresses +and routes continuously. This option has a slightly different format. +Namely, +the \verb|monitor| command is the first in the command line and then +the object list follows: +\begin{verbatim} + ip monitor [ file FILE ] [ all | OBJECT-LIST ] +\end{verbatim} +\verb|OBJECT-LIST| is the list of object types that we want to monitor. +It may contain \verb|link|, \verb|address| and \verb|route|. +If no \verb|file| argument is given, \verb|ip| opens RTNETLINK, +listens on it and dumps state changes in the format described +in previous sections. + +If a file name is given, it does not listen on RTNETLINK, +but opens the file containing RTNETLINK messages saved in binary format +and dumps them. Such a history file can be generated with the +\verb|rtmon| utility. This utility has a command line syntax similar to +\verb|ip monitor|. +Ideally, \verb|rtmon| should be started before +the first network configuration command is issued. F.e.\ if +you insert: +\begin{verbatim} + rtmon file /var/log/rtmon.log +\end{verbatim} +in a startup script, you will be able to view the full history +later. + +Certainly, it is possible to start \verb|rtmon| at any time. +It prepends the history with the state snapshot dumped at the moment +of starting. + + +\section{Route realms and policy propagation, {\tt rtacct}} +\label{RT-REALMS} + +On routers using OSPF ASE or, especially, the BGP protocol, routing +tables may be huge. If we want to classify or to account for the packets +per route, we will have to keep lots of information. Even worse, if we +want to distinguish the packets not only by their destination, but +also by their source, the task gets quadratic complexity and its solution +is physically impossible. + +One approach to propagating the policy from routing protocols +to the forwarding engine has been proposed in~\cite{IOS-BGP-PP}. +Essentially, Cisco Policy Propagation via BGP is based on the fact +that dedicated routers all have the RIB (Routing Information Base) +close to the forwarding engine, so policy routing rules can +check all the route attributes, including ASPATH information +and community strings. + +The Linux architecture, splitting the RIB (maintained by a user level +daemon) and the kernel based FIB (Forwarding Information Base), +does not allow such a simple approach. + +It is to our fortune because there is another solution +which allows even more flexible policy and richer semantics. + +Namely, routes can be clustered together in user space, based on their +attributes. F.e.\ a BGP router knows route ASPATH, its community; +an OSPF router knows the route tag or its area. The administrator, when adding +routes manually, also knows their nature. Providing that the number of such +aggregates (we call them {\em realms\/}) is low, the task of full +classification both by source and destination becomes quite manageable. + +So each route may be assigned to a realm. It is assumed that +this identification is made by a routing daemon, but static routes +can also be handled manually with \verb|ip route| (see sec.\ref{IP-ROUTE}, +p.\pageref{IP-ROUTE}). +\begin{NB} + There is a patch to \verb|gated|, allowing classification of routes + to realms with all the set of policy rules implemented in \verb|gated|: + by prefix, by ASPATH, by origin, by tag etc. +\end{NB} + +To facilitate the construction (f.e.\ in case the routing +daemon is not aware of realms), missing realms may be completed +with routing policy rules, see sec.~\ref{IP-RULE}, p.\pageref{IP-RULE}. + +For each packet the kernel calculates a tuple of realms: source realm +and destination realm, using the following algorithm: + +\begin{enumerate} +\item If the route has a realm, the destination realm of the packet is set to it. +\item If the rule has a source realm, the source realm of the packet is set to it. +If the destination realm was not inherited from the route and the rule has a destination realm, +it is also set. +\item If at least one of the realms is still unknown, the kernel finds +the reversed route to the source of the packet. +\item If the source realm is still unknown, get it from the reversed route. +\item If one of the realms is still unknown, swap the realms of reversed +routes and apply step 2 again. +\end{enumerate} + +After this procedure is completed we know what realm the packet +arrived from and the realm where it is going to propagate to. +If some of the realms are unknown, they are initialized to zero +(or realm \verb|unknown|). + +The main application of realms is the TC \verb|route| classifier~\cite{TC-CREF}, +where they are used to help assign packets to traffic classes, +to account, police and schedule them according to this +classification. + +A much simpler but still very useful application is incoming packet +accounting by realms. The kernel gathers a packet statistics summary +which can be viewed with the \verb|rtacct| utility. +\begin{verbatim} +kuznet@amber:~ $ rtacct russia +Realm BytesTo PktsTo BytesFrom PktsFrom +russia 20576778 169176 47080168 153805 +kuznet@amber:~ $ +\end{verbatim} +This shows that this router received 153805 packets from +the realm \verb|russia| and forwarded 169176 packets to \verb|russia|. +The realm \verb|russia| consists of routes with ASPATHs not leaving +Russia. + +Note that locally originating packets are not accounted here, +\verb|rtacct| shows incoming packets only. Using the \verb|route| +classifier (see~\cite{TC-CREF}) you can get even more detailed +accounting information about outgoing packets, optionally +summarizing traffic not only by source or destination, but +by any pair of source and destination realms. + + +\begin{thebibliography}{99} +\addcontentsline{toc}{section}{References} +\bibitem{RFC-NDISC} T.~Narten, E.~Nordmark, W.~Simpson. +``Neighbor Discovery for IP Version 6 (IPv6)'', RFC-2461. + +\bibitem{RFC-ADDRCONF} S.~Thomson, T.~Narten. +``IPv6 Stateless Address Autoconfiguration'', RFC-2462. + +\bibitem{RFC1812} F.~Baker. +``Requirements for IP Version 4 Routers'', RFC-1812. + +\bibitem{RFC1122} R.~T.~Braden. +``Requirements for Internet hosts --- communication layers'', RFC-1122. + +\bibitem{IOS} ``Cisco IOS Release 12.0 Network Protocols +Command Reference, Part 1'' and +``Cisco IOS Release 12.0 Quality of Service Solutions +Configuration Guide: Configuring Policy-Based Routing'',\\ +http://www.cisco.com/univercd/cc/td/doc/product/software/ios120. + +\bibitem{IP-TUNNELS} A.~N.~Kuznetsov. +``Tunnels over IP in Linux-2.2'', \\ +In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}. + +\bibitem{TC-CREF} A.~N.~Kuznetsov. ``TC Command Reference'',\\ +In: {\tt ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz}. + +\bibitem{IOS-BGP-PP} ``Cisco IOS Release 12.0 Quality of Service Solutions +Configuration Guide: Configuring QoS Policy Propagation via +Border Gateway Protocol'',\\ +http://www.cisco.com/univercd/cc/td/doc/product/software/ios120. + +\bibitem{RFC-DHCP} R.~Droms. +``Dynamic Host Configuration Protocol.'', RFC-2131 + +\end{thebibliography} + + + + +\appendix +\addcontentsline{toc}{section}{Appendix} + +\section{Source address selection} +\label{ADDR-SEL} + +When a host creates an IP packet, it must select some source +address. Correct source address selection is a critical procedure, +because it gives the receiver the information needed to deliver a +reply. If the source is selected incorrectly, in the best case, +the backward path may appear different to the forward one which +is harmful for performance. In the worst case, when the addresses +are administratively scoped, the reply may be lost entirely. + +Linux-2.2 selects source addresses using the following algorithm: + +\begin{itemize} +\item +The application may select a source address explicitly with \verb|bind(2)| +syscall or supplying it to \verb|sendmsg(2)| via the ancillary data object +\verb|IP_PKTINFO|. In this case the kernel only checks the validity +of the address and never tries to ``improve'' an incorrect user choice, +generating an error instead. +\begin{NB} + Never say ``Never''. The sysctl option \verb|ip_dynaddr| breaks + this axiom. It has been made deliberately with the purpose + of automatically reselecting the address on hosts with dynamic dial-out interfaces. + However, this hack {\em must not\/} be used on multihomed hosts + and especially on routers: it would break them. +\end{NB} + + +\item Otherwise, IP routing tables can contain an explicit source +address hint for this destination. The hint is set with the \verb|src| parameter +to the \verb|ip route| command, sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}. + + +\item Otherwise, the kernel searches through the list of addresses +attached to the interface through which the packets will be routed. +The search strategies are different for IP and IPv6. Namely: + +\begin{itemize} +\item IPv6 searches for the first valid, not deprecated address +with the same scope as the destination. + +\item IP searches for the first valid address with a scope wider +than the scope of the destination but it prefers addresses +which fall to the same subnet as the nexthop of the route +to the destination. Unlike IPv6, the scopes of IPv4 destinations +are not encoded in their addresses but are supplied +in routing tables instead (the \verb|scope| parameter to the \verb|ip route| command, +sec.\ref{IP-ROUTE}, p.\pageref{IP-ROUTE}). + +\end{itemize} + + +\item Otherwise, if the scope of the destination is \verb|link| or \verb|host|, +the algorithm fails and returns a zero source address. + +\item Otherwise, all interfaces are scanned to search for an address +with an appropriate scope. The loopback device \verb|lo| is always the first +in the search list, so that if an address with global scope (not 127.0.0.1!) +is configured on loopback, it is always preferred. + +\end{itemize} + + +\section{Proxy ARP/NDISC} +\label{PROXY-NEIGH} + +Routers may answer ARP/NDISC solicitations on behalf of other hosts. +In Linux-2.2 proxy ARP on an interface may be enabled +by setting the kernel \verb|sysctl| variable +\verb|/proc/sys/net/ipv4/conf/<dev>/proxy_arp| to 1. After this, the router +starts to answer ARP requests on the interface \verb|<dev>|, provided +the route to the requested destination does {\em not\/} go back via the same +device. + +The variable \verb|/proc/sys/net/ipv4/conf/all/proxy_arp| enables proxy +ARP on all the IP devices. + +However, this approach fails in the case of IPv6 because the router +must join the solicited node multicast address to listen for the corresponding +NDISC queries. It means that proxy NDISC is possible only on a per destination +basis. + +Logically, proxy ARP/NDISC is not a kernel task. It can easily be implemented +in user space. However, similar functionality was present in BSD kernels +and in Linux-2.0, so we have to preserve it at least to the extent that +is standardized in BSD. +\begin{NB} + Linux-2.0 ARP had a feature called {\em subnet\/} proxy ARP. + It is replaced with the sysctl flag in Linux-2.2. +\end{NB} + + +The \verb|ip| utility provides a way to manage proxy ARP/NDISC +with the \verb|ip neigh| command, namely: +\begin{verbatim} + ip neigh add proxy ADDRESS [ dev NAME ] +\end{verbatim} +adds a new proxy ARP/NDISC record and +\begin{verbatim} + ip neigh del proxy ADDRESS [ dev NAME ] +\end{verbatim} +deletes it. + +If the name of the device is not given, the router will answer solicitations +for address \verb|ADDRESS| on all devices, otherwise it will only serve +the device \verb|NAME|. Even if the proxy entry is created with +\verb|ip neigh|, the router {\em will not\/} answer a query if the route +to the destination goes back via the interface from which the solicitation +was received. + +It is important to emphasize that proxy entries have {\em no\/} +parameters other than these (IP/IPv6 address and optional device). +Particularly, the entry does not store any link layer address. +It always advertises the station address of the interface +on which it sends advertisements (i.e. it's own station address). + +\section{Route NAT status} +\label{ROUTE-NAT} + +NAT (or ``Network Address Translation'') remaps some parts +of the IP address space into other ones. Linux-2.2 route NAT is supposed +to be used to facilitate policy routing by rewriting addresses +to other routing domains or to help while renumbering sites +to another prefix. + +\paragraph{What it is not:} +It is necessary to emphasize that {\em it is not supposed\/} +to be used to compress address space or to split load. +This is not missing functionality but a design principle. +Route NAT is {\em stateless\/}. It does not hold any state +about translated sessions. This means that it handles any number +of sessions flawlessly. But it also means that it is {\em static\/}. +It cannot detect the moment when the last TCP client stops +using an address. For the same reason, it will not help to split +load between several servers. +\begin{NB} +It is a pretty commonly held belief that it is useful to split load between +several servers with NAT. This is a mistake. All you get from this +is the requirement that the router keep the state of all the TCP connections +going via it. Well, if the router is so powerful, run apache on it. 8) +\end{NB} + +The second feature: it does not touch packet payload, +does not try to ``improve'' broken protocols by looking +through its data and mangling it. It mangles IP addresses, +only IP addresses and nothing but IP addresses. +This also, is not missing any functionality. + +To resume: if you need to compress address space or keep +active FTP clients happy, your choice is not route NAT but masquerading, +port forwarding, NAPT etc. +\begin{NB} +By the way, you may also want to look at +http://www.suse.com/\~mha/HyperNews/get/linux-ip-nat.html +\end{NB} + + +\paragraph{How it works.} +Some part of the address space is reserved for dummy addresses +which will look for all the world like some host addresses +inside your network. No other hosts may use these addresses, +however other routers may also be configured to translate them. +\begin{NB} +A great advantage of route NAT is that it may be used not +only in stub networks but in environments with arbitrarily complicated +structure. It does not firewall, it {\em forwards.} +\end{NB} +These addresses are selected by the \verb|ip route| command +(sec.\ref{IP-ROUTE-ADD}, p.\pageref{IP-ROUTE-ADD}). F.e.\ +\begin{verbatim} + ip route add nat 192.203.80.144 via 193.233.7.83 +\end{verbatim} +states that the single address 192.203.80.144 is a dummy NAT address. +For all the world it looks like a host address inside our network. +For neighbouring hosts and routers it looks like the local address +of the translating router. The router answers ARP for it, advertises +this address as routed via it, {\em et al\/}. When the router +receives a packet destined for 192.203.80.144, it replaces +this address with 193.233.7.83 which is the address of some real +host and forwards the packet. If you need to remap +blocks of addresses, you may use a command like: +\begin{verbatim} + ip route add nat 192.203.80.192/26 via 193.233.7.64 +\end{verbatim} +This command will map a block of 63 addresses 192.203.80.192-255 to +193.233.7.64-127. + +When an internal host (193.233.7.83 in the example above) +sends something to the outer world and these packets are forwarded +by our router, it should translate the source address 193.233.7.83 +into 192.203.80.144. This task is solved by setting a special +policy rule (sec.\ref{IP-RULE-ADD}, p.\pageref{IP-RULE-ADD}): +\begin{verbatim} + ip rule add prio 320 from 193.233.7.83 nat 192.203.80.144 +\end{verbatim} +This rule says that the source address 193.233.7.83 +should be translated into 192.203.80.144 before forwarding. +It is important that the address after the \verb|nat| keyword +is some NAT address, declared by {\tt ip route add nat}. +If it is just a random address the router will not map to it. +\begin{NB} +The exception is when the address is a local address of this +router (or 0.0.0.0) and masquerading is configured in the linux-2.2 +kernel. In this case the router will masquerade the packets as this address. +If 0.0.0.0 is selected, the result is equivalent to one +obtained with firewalling rules. Otherwise, you have the way +to order Linux to masquerade to this fixed address. +NAT mechanism used in linux-2.4 is more flexible than +masquerading, so that this feature has lost meaning and disabled. +\end{NB} + +If the network has non-trivial internal structure, it is +useful and even necessary to add rules disabling translation +when a packet does not leave this network. Let us return to the +example from sec.\ref{IP-RULE-SHOW} (p.\pageref{IP-RULE-SHOW}). +\begin{verbatim} +300: from 193.233.7.83 to 193.233.7.0/24 lookup main +310: from 193.233.7.83 to 192.203.80.0/24 lookup main +320: from 193.233.7.83 lookup inr.ruhep map-to 192.203.80.144 +\end{verbatim} +This block of rules causes normal forwarding when +packets from 193.233.7.83 do not leave networks 193.233.7/24 +and 192.203.80/24. Also, if the \verb|inr.ruhep| table does not +contain a route to the destination (which means that the routing +domain owning addresses from 192.203.80/24 is dead), no translation +will occur. Otherwise, the packets are translated. + +\paragraph{How to only translate selected ports:} +If you only want to translate selected ports (f.e.\ http) +and leave the rest intact, you may use \verb|ipchains| +to \verb|fwmark| a class of packets. +Suppose you did and all the packets from 193.233.7.83 +destined for port 80 are marked with marker 0x1234 in input fwchain. +In this case you may replace rule \#320 with: +\begin{verbatim} +320: from 193.233.7.83 fwmark 1234 lookup main map-to 192.203.80.144 +\end{verbatim} +and translation will only be enabled for outgoing http requests. + +\section{Example: minimal host setup} +\label{EXAMPLE-SETUP} + +The following script gives an example of a fault safe +setup of IP (and IPv6, if it is compiled into the kernel) +in the common case of a node attached to a single broadcast +network. A more advanced script, which may be used both on multihomed +hosts and on routers, is described in the following +section. + +The utilities used in the script may be found in the +directory ftp://ftp.inr.ac.ru/ip-routing/: +\begin{enumerate} +\item \verb|ip| --- package \verb|iproute2|. +\item \verb|arping| --- package \verb|iputils|. +\item \verb|rdisc| --- package \verb|iputils|. +\end{enumerate} +\begin{NB} +It also refers to a DHCP client, \verb|dhcpcd|. I should refrain from +recommending a good DHCP client to use. All that I can +say is that ISC \verb|dhcp-2.0b1pl6| patched with the patch that +can be found in the \verb|dhcp.bootp.rarp| subdirectory of +the same ftp site {\em does\/} work, +at least on Ethernet and Token Ring. +\end{NB} + +\begin{verbatim} +#! /bin/bash +\end{verbatim} +\begin{flushleft} +\# {\bf Usage: \verb|ifone ADDRESS[/PREFIX-LENGTH] [DEVICE]|}\\ +\# {\bf Parameters:}\\ +\# \$1 --- Static IP address, optionally followed by prefix length.\\ +\# \$2 --- Device name. If it is missing, \verb|eth0| is asssumed.\\ +\# F.e. \verb|ifone 193.233.7.90| +\end{flushleft} +\begin{verbatim} +dev=$2 +: ${dev:=eth0} +ipaddr= +\end{verbatim} +\# Parse IP address, splitting prefix length. +\begin{verbatim} +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + : ${pfxlen:=24} +fi +pfx="${ipaddr}/${pfxlen}" +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 0} --- enable loopback.\\ +\#\\ +\# This step is necessary on any networked box before attempt\\ +\# to configure any other device.\\ +\end{flushleft} +\begin{verbatim} +ip link set up dev lo +ip addr add 127.0.0.1/8 dev lo brd + scope host +\end{verbatim} +\begin{flushleft} +\# IPv6 autoconfigure themself on loopback.\\ +\#\\ +\# If user gave loopback as device, we add the address as alias and exit. +\end{flushleft} +\begin{verbatim} +if [ "$dev" = "lo" ]; then + if [ "$ipaddr" != "" -a "$ipaddr" != "127.0.0.1" ]; then + ip address add $ipaddr dev $dev + exit $? + fi + exit 0 +fi +\end{verbatim} + +\noindent\# {\bf Step 1} --- enable device \verb|$dev| + +\begin{verbatim} +if ! ip link set up dev $dev ; then + echo "Cannot enable interface $dev. Aborting." 1>&2 + exit 1 +fi +\end{verbatim} +\begin{flushleft} +\# The interface is \verb|UP|. IPv6 started stateless autoconfiguration itself,\\ +\# and its configuration finishes here. However,\\ +\# IP still needs some static preconfigured address. +\end{flushleft} +\begin{verbatim} +if [ "$ipaddr" = "" ]; then + echo "No address for $dev is configured, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 2} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\ +\# Send two probes and wait for result for 3 seconds.\\ +\# If the interface opens slower f.e.\ due to long media detection,\\ +\# you want to increase the timeout.\\ +\end{flushleft} +\begin{verbatim} +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Address $ipaddr is busy, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} +\begin{flushleft} +\# OK, the address is unique, we may add it on the interface.\\ +\#\\ +\# {\bf Step 3} --- Configure the address on the interface. +\end{flushleft} + +\begin{verbatim} +if ! ip address add $pfx brd + dev $dev; then + echo "Failed to add $pfx on $dev, trying DHCP..." 1>&2 + dhcpcd + exit $? +fi +\end{verbatim} + +\noindent\# {\bf Step 4} --- Announce our presence on the link. +\begin{verbatim} +arping -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2; + arping -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 5} (optional) --- Add some control routes.\\ +\#\\ +\# 1. Prohibit link local multicast addresses.\\ +\# 2. Prohibit link local (alias, limited) broadcast.\\ +\# 3. Add default multicast route. +\end{flushleft} +\begin{verbatim} +ip route add unreachable 224.0.0.0/24 +ip route add unreachable 255.255.255.255 +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 6} --- Add fallback default route with huge metric.\\ +\# If a proxy ARP server is present on the interface, we will be\\ +\# able to talk to all the Internet without further configuration.\\ +\# It is not so cheap though and we still hope that this route\\ +\# will be overridden by more correct one by rdisc.\\ +\# Do not make this step if the device is not ARPable,\\ +\# because dead nexthop detection does not work on them. +\end{flushleft} +\begin{verbatim} +if [ "$noarp" = "0" ]; then + ip ro add default dev $dev metric 30000 scope global +fi +\end{verbatim} + +\begin{flushleft} +\# {\bf Step 7} --- Restart router discovery and exit. +\end{flushleft} +\begin{verbatim} +killall -HUP rdisc || rdisc -fs +exit 0 +\end{verbatim} + + +\section{Example: {\protect\tt ifcfg} --- interface address management} +\label{EXAMPLE-IFCFG} + +This is a simplistic script replacing one option of \verb|ifconfig|, +namely, IP address management. It not only adds +addresses, but also carries out Duplicate Address Detection~\cite{RFC-DHCP}, +sends unsolicited ARP to update the caches of other hosts sharing +the interface, adds some control routes and restarts Router Discovery +when it is necessary. + +I strongly recommend using it {\em instead\/} of \verb|ifconfig| both +on hosts and on routers. + +\begin{verbatim} +#! /bin/bash +\end{verbatim} +\begin{flushleft} +\# {\bf Usage: \verb?ifcfg DEVICE[:ALIAS] [add|del] ADDRESS[/LENGTH] [PEER]?}\\ +\# {\bf Parameters:}\\ +\# ---Device name. It may have alias suffix, separated by colon.\\ +\# ---Command: add, delete or stop.\\ +\# ---IP address, optionally followed by prefix length.\\ +\# ---Optional peer address for pointopoint interfaces.\\ +\# F.e. \verb|ifcfg eth0 193.233.7.90/24| + +\noindent\# This function determines, whether it is router or host.\\ +\# It returns 0, if the host is apparently not router. +\end{flushleft} +\begin{verbatim} +CheckForwarding () { + local sbase fwd + sbase=/proc/sys/net/ipv4/conf + fwd=0 + if [ -d $sbase ]; then + for dir in $sbase/*/forwarding; do + fwd=$[$fwd + `cat $dir`] + done + else + fwd=2 + fi + return $fwd +} +\end{verbatim} +\begin{flushleft} +\# This function restarts Router Discovery.\\ +\end{flushleft} +\begin{verbatim} +RestartRDISC () { + killall -HUP rdisc || rdisc -fs +} +\end{verbatim} +\begin{flushleft} +\# Calculate ABC "natural" mask length\\ +\# Arg: \$1 = dotquad address +\end{flushleft} +\begin{verbatim} +ABCMaskLen () { + local class; + class=${1%%.*} + if [ $class -eq 0 -o $class -ge 224 ]; then return 0 + elif [ $class -ge 192 ]; then return 24 + elif [ $class -ge 128 ]; then return 16 + else return 8 ; fi +} +\end{verbatim} + + +\begin{flushleft} +\# {\bf MAIN()}\\ +\#\\ +\# Strip alias suffix separated by colon. +\end{flushleft} +\begin{verbatim} +label="label $1" +ldev=$1 +dev=${1%:*} +if [ "$dev" = "" -o "$1" = "help" ]; then + echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2 + echo " add - add new address" 1>&2 + echo " del - delete address" 1>&2 + echo " stop - completely disable IP" 1>&2 + exit 1 +fi +shift + +CheckForwarding +fwd=$? +\end{verbatim} +\begin{flushleft} +\# Parse command. If it is ``stop'', flush and exit. +\end{flushleft} +\begin{verbatim} +deleting=0 +case "$1" in +add) shift ;; +stop) + if [ "$ldev" != "$dev" ]; then + echo "Cannot stop alias $ldev" 1>&2 + exit 1; + fi + ip -4 addr flush dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 ;; +del*) + deleting=1; shift ;; +*) +esac +\end{verbatim} +\begin{flushleft} +\# Parse prefix, split prefix length, separated by slash. +\end{flushleft} +\begin{verbatim} +ipaddr= +pfxlen= +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + if [ "$ipaddr" = "" ]; then + echo "$1 is bad IP address." 1>&2 + exit 1 + fi +fi +shift +\end{verbatim} +\begin{flushleft} +\# If peer address is present, prefix length is 32.\\ +\# Otherwise, if prefix length was not given, guess it. +\end{flushleft} +\begin{verbatim} +peer=$1 +if [ "$peer" != "" ]; then + if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then + echo "Peer address with non-trivial netmask." 1>&2 + exit 1 + fi + pfx="$ipaddr peer $peer" +else + if [ "$pfxlen" = "" ]; then + ABCMaskLen $ipaddr + pfxlen=$? + fi + pfx="$ipaddr/$pfxlen" +fi +if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then + label= +fi +\end{verbatim} +\begin{flushleft} +\# If deletion was requested, delete the address and restart RDISC +\end{flushleft} +\begin{verbatim} +if [ $deleting -ne 0 ]; then + ip addr del $pfx dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 +fi +\end{verbatim} +\begin{flushleft} +\# Start interface initialization.\\ +\#\\ +\# {\bf Step 0} --- enable device \verb|$dev| +\end{flushleft} +\begin{verbatim} +if ! ip link set up dev $dev ; then + echo "Error: cannot enable interface $dev." 1>&2 + exit 1 +fi +if [ "$ipaddr" = "" ]; then exit 0; fi +\end{verbatim} +\begin{flushleft} +\# {\bf Step 1} --- IP Duplicate Address Detection~\cite{RFC-DHCP}.\\ +\# Send two probes and wait for result for 3 seconds.\\ +\# If the interface opens slower f.e.\ due to long media detection,\\ +\# you want to increase the timeout.\\ +\end{flushleft} +\begin{verbatim} +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Error: some host already uses address $ipaddr on $dev." 1>&2 + exit 1 +fi +\end{verbatim} +\begin{flushleft} +\# OK, the address is unique. We may add it to the interface.\\ +\#\\ +\# {\bf Step 2} --- Configure the address on the interface. +\end{flushleft} +\begin{verbatim} +if ! ip address add $pfx brd + dev $dev $label; then + echo "Error: failed to add $pfx on $dev." 1>&2 + exit 1 +fi +\end{verbatim} +\noindent\# {\bf Step 3} --- Announce our presence on the link +\begin{verbatim} +arping -q -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2 ; + arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & +\end{verbatim} +\begin{flushleft} +\# {\bf Step 4} (optional) --- Add some control routes.\\ +\#\\ +\# 1. Prohibit link local multicast addresses.\\ +\# 2. Prohibit link local (alias, limited) broadcast.\\ +\# 3. Add default multicast route. +\end{flushleft} +\begin{verbatim} +ip route add unreachable 224.0.0.0/24 >& /dev/null +ip route add unreachable 255.255.255.255 >& /dev/null +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null +fi +\end{verbatim} +\begin{flushleft} +\# {\bf Step 5} --- Add fallback default route with huge metric.\\ +\# If a proxy ARP server is present on the interface, we will be\\ +\# able to talk to all the Internet without further configuration.\\ +\# Do not make this step on router or if the device is not ARPable.\\ +\# because dead nexthop detection does not work on them. +\end{flushleft} +\begin{verbatim} +if [ $fwd -eq 0 ]; then + if [ $noarp -eq 0 ]; then + ip ro append default dev $dev metric 30000 scope global + elif [ "$peer" != "" ]; then + if ping -q -c 2 -w 4 $peer ; then + ip ro append default via $peer dev $dev metric 30001 + fi + fi + RestartRDISC +fi + +exit 0 +\end{verbatim} +\begin{flushleft} +\# End of {\bf MAIN()} +\end{flushleft} + + +\end{document} diff --git a/doc/ip-tunnels.tex b/doc/ip-tunnels.tex new file mode 100644 index 0000000..0a8c930 --- /dev/null +++ b/doc/ip-tunnels.tex @@ -0,0 +1,469 @@ +\documentstyle[12pt,twoside]{article} +\def\TITLE{Tunnels over IP} +\input preamble +\begin{center} +\Large\bf Tunnels over IP in Linux-2.2 +\end{center} + + +\begin{center} +{ \large Alexey~N.~Kuznetsov } \\ +\em Institute for Nuclear Research, Moscow \\ +\verb|kuznet@ms2.inr.ac.ru| \\ +\rm March 17, 1999 +\end{center} + +\vspace{5mm} + +\tableofcontents + + +\section{Instead of introduction: micro-FAQ.} + +\begin{itemize} + +\item +Q: In linux-2.0.36 I used: +\begin{verbatim} + ifconfig tunl1 10.0.0.1 pointopoint 193.233.7.65 +\end{verbatim} +to create tunnel. It does not work in 2.2.0! + +A: You are right, it does not work. The command written above is split to two commands. +\begin{verbatim} + ip tunnel add MY-TUNNEL mode ipip remote 193.233.7.65 +\end{verbatim} +will create tunnel device with name \verb|MY-TUNNEL|. Now you may configure +it with: +\begin{verbatim} + ifconfig MY-TUNNEL 10.0.0.1 +\end{verbatim} +Certainly, if you prefer name \verb|tunl1| to \verb|MY-TUNNEL|, +you still may use it. + +\item +Q: In linux-2.0.36 I used: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 + route add -net 10.0.0.0 gw 193.233.7.65 dev tunl0 +\end{verbatim} +to tunnel net 10.0.0.0 via router 193.233.7.65. It does not +work in 2.2.0! Moreover, \verb|route| prints a funny error sort of +``network unreachable'' and after this I found a strange direct route +to 10.0.0.0 via \verb|tunl0| in routing table. + +A: Yes, in 2.2 the rule that {\em normal} gateway must reside on directly +connected network has not any exceptions. You may tell kernel, that +this particular route is {\em abnormal}: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 netmask 255.255.255.255 + ip route add 10.0.0.0/8 via 193.233.7.65 dev tunl0 onlink +\end{verbatim} +Note keyword \verb|onlink|, it is the magic key that orders kernel +not to check for consistency of gateway address. +Probably, after this explanation you have already guessed another method +to cheat kernel: +\begin{verbatim} + ifconfig tunl0 10.0.0.1 netmask 255.255.255.255 + route add -host 193.233.7.65 dev tunl0 + route add -net 10.0.0.0 netmask 255.0.0.0 gw 193.233.7.65 + route del -host 193.233.7.65 dev tunl0 +\end{verbatim} +Well, if you like such tricks, nobody may prohibit you to use them. +Only do not forget +that between \verb|route add| and \verb|route del| host 193.233.7.65 is +unreachable. + +\item +Q: In 2.0.36 I used to load \verb|tunnel| device module and \verb|ipip| module. +I cannot find any \verb|tunnel| in 2.2! + +A: Linux-2.2 has single module \verb|ipip| for both directions of tunneling +and for all IPIP tunnel devices. + +\item +Q: \verb|traceroute| does not work over tunnel! Well, stop... It works, + only skips some number of hops. + +A: Yes. By default tunnel driver copies \verb|ttl| value from +inner packet to outer one. It means that path traversed by tunneled +packets to another endpoint is not hidden. If you dislike this, or if you +are going to use some routing protocol expecting that packets +with ttl 1 will reach peering host (f.e.\ RIP, OSPF or EBGP) +and you are not afraid of +tunnel loops, you may append option \verb|ttl 64|, when creating tunnel +with \verb|ip tunnel add|. + +\item +Q: ... Well, list of things, which 2.0 was able to do finishes. + +\end{itemize} + +\paragraph{Summary of differences between 2.2 and 2.0.} + +\begin{itemize} + +\item {\bf In 2.0} you could compile tunnel device into kernel + and got set of 4 devices \verb|tunl0| ... \verb|tunl3| or, + alternatively, compile it as module and load new module + for each new tunnel. Also, module \verb|ipip| was necessary + to receive tunneled packets. + + {\bf 2.2} has {\em one\/} module \verb|ipip|. Loading it you get base + tunnel device \verb|tunl0| and another tunnels may be created with command + \verb|ip tunnel add|. These new devices may have arbitrary names. + + +\item {\bf In 2.0} you set remote tunnel endpoint address with + the command \verb|ifconfig| ... \verb|pointopoint A|. + + {\bf In 2.2} this command has the same semantics on all + the interfaces, namely it sets not tunnel endpoint, + but address of peering host, which is directly reachable + via this tunnel, + rather than via Internet. Actual tunnel endpoint address \verb|A| + should be set with \verb|ip tunnel add ... remote A|. + +\item {\bf In 2.0} you create tunnel routes with the command: +\begin{verbatim} + route add -net 10.0.0.0 gw A dev tunl0 +\end{verbatim} + + {\bf 2.2} interprets this command equally for all device + kinds and gateway is required to be directly reachable via this tunnel, + rather than via Internet. You still may use \verb|ip route add ... onlink| + to override this behaviour. + +\end{itemize} + + +\section{Tunnel setup: basics} + +Standard Linux-2.2 kernel supports three flavor of tunnels, +listed in the following table: +\vspace{2mm} + +\begin{tabular}{lll} +\vrule depth 0.8ex width 0pt\relax +Mode & Description & Base device \\ +ipip & IP over IP & tunl0 \\ +sit & IPv6 over IP & sit0 \\ +gre & ANY over GRE over IP & gre0 +\end{tabular} + +\vspace{2mm} + +\noindent All the kinds of tunnels are created with one command: +\begin{verbatim} + ip tunnel add <NAME> mode <MODE> [ local <S> ] [ remote <D> ] +\end{verbatim} + +This command creates new tunnel device with name \verb|<NAME>|. +The \verb|<NAME>| is an arbitrary string. Particularly, +it may be even \verb|eth0|. The rest of parameters set +different tunnel characteristics. + +\begin{itemize} + +\item +\verb|mode <MODE>| sets tunnel mode. Three modes are available now + \verb|ipip|, \verb|sit| and \verb|gre|. + +\item +\verb|remote <D>| sets remote endpoint of the tunnel to IP + address \verb|<D>|. +\item +\verb|local <S>| sets fixed local address for tunneled + packets. It must be an address on another interface of this host. + +\end{itemize} + +\let\thefootnote\oldthefootnote + +Both \verb|remote| and \verb|local| may be omitted. In this case we +say that they are zero or wildcard. Two tunnels of one mode cannot +have the same \verb|remote| and \verb|local|. Particularly it means +that base device or fallback tunnel cannot be replicated.\footnote{ +This restriction is relaxed for keyed GRE tunnels.} + +Tunnels are divided to two classes: {\bf pointopoint} tunnels, which +have some not wildcard \verb|remote| address and deliver all the packets +to this destination, and {\bf NBMA} (i.e. Non-Broadcast Multi-Access) tunnels, +which have no \verb|remote|. Particularly, base devices (f.e.\ \verb|tunl0|) +are NBMA, because they have neither \verb|remote| nor +\verb|local| addresses. + + +After tunnel device is created you should configure it as you did +it with another devices. Certainly, the configuration of tunnels has +some features related to the fact that they work over existing Internet +routing infrastructure and simultaneously create new virtual links, +which changes this infrastructure. The danger that not enough careful +tunnel setup will result in formation of tunnel loops, +collapse of routing or flooding network with exponentially +growing number of tunneled fragments is very real. + + +Protocol setup on pointopoint tunnels does not differ of configuration +of another devices. You should set a protocol address with \verb|ifconfig| +and add routes with \verb|route| utility. + +NBMA tunnels are different. To route something via NBMA tunnel +you have to explain to driver, where it should deliver packets to. +The only way to make it is to create special routes with gateway +address pointing to desired endpoint. F.e.\ +\begin{verbatim} + ip route add 10.0.0.0/24 via <A> dev tunl0 onlink +\end{verbatim} +It is important to use option \verb|onlink|, otherwise +kernel will refuse request to create route via gateway not directly +reachable over device \verb|tunl0|. With IPv6 the situation is much simpler: +when you start device \verb|sit0|, it automatically configures itself +with all IPv4 addresses mapped to IPv6 space, so that all IPv4 +Internet is {\em really reachable} via \verb|sit0|! Excellent, the command +\begin{verbatim} + ip route add 3FFE::/16 via ::193.233.7.65 dev sit0 +\end{verbatim} +will route \verb|3FFE::/16| via \verb|sit0|, sending all the packets +destined to this prefix to 193.233.7.65. + +\section{Tunnel setup: options} + +Command \verb|ip tunnel add| has several additional options. +\begin{itemize} + +\item \verb|ttl N| --- set fixed TTL \verb|N| on tunneled packets. + \verb|N| is number in the range 1--255. 0 is special value, + meaning that packets inherit TTL value. + Default value is: \verb|inherit|. + +\item \verb|tos T| --- set fixed tos \verb|T| on tunneled packets. + Default value is: \verb|inherit|. + +\item \verb|dev DEV| --- bind tunnel to device \verb|DEV|, so that + tunneled packets will be routed only via this device and will + not be able to escape to another device, when route to endpoint changes. + +\item \verb|nopmtudisc| --- disable Path MTU Discovery on this tunnel. + It is enabled by default. Note that fixed ttl is incompatible + with this option: tunnels with fixed ttl always make pmtu discovery. + +\end{itemize} + +\verb|ipip| and \verb|sit| tunnels have no more options. \verb|gre| +tunnels are more complicated: + +\begin{itemize} + +\item \verb|key K| --- use keyed GRE with key \verb|K|. \verb|K| is + either number or IP address-like dotted quad. + +\item \verb|csum| --- checksum tunneled packets. + +\item \verb|seq| --- serialize packets. +\begin{NB} + I think this option does not + work. At least, I did not test it, did not debug it and + even do not understand, how it is supposed to work and for what + purpose Cisco planned to use it. +\end{NB} + +\end{itemize} + + +Actually, these GRE options can be set separately for input and +output directions by prefixing corresponding keywords with letter +\verb|i| or \verb|o|. F.e.\ \verb|icsum| orders to accept only +packets with correct checksum and \verb|ocsum| means, that +our host will calculate and send checksum. + +Command \verb|ip tunnel add| is not the only operation, +which can be made with tunnels. Certainly, you may get short help page +with: +\begin{verbatim} + ip tunnel help +\end{verbatim} + +Besides that, you may view list of installed tunnels with the help of command: +\begin{verbatim} + ip tunnel ls +\end{verbatim} +Also you may look at statistics: +\begin{verbatim} + ip -s tunnel ls Cisco +\end{verbatim} +where \verb|Cisco| is name of tunnel device. Command +\begin{verbatim} + ip tunnel del Cisco +\end{verbatim} +destroys tunnel \verb|Cisco|. And, finally, +\begin{verbatim} + ip tunnel change Cisco mode sit local ME remote HE ttl 32 +\end{verbatim} +changes its parameters. + +\section{Differences 2.2 and 2.0 tunnels revisited.} + +Now we can discuss more subtle differences between tunneling in 2.0 +and 2.2. + +\begin{itemize} + +\item In 2.0 all tunneled packets were received promiscuously +as soon as you loaded module \verb|ipip|. 2.2 tries to select the best +tunnel device and packet looks as received on this. F.e.\ if host +received \verb|ipip| packet from host \verb|D| destined to our +local address \verb|S|, kernel searches for matching tunnels +in order: + +\begin{tabular}{ll} +1 & \verb|remote| is \verb|D| and \verb|local| is \verb|S| \\ +2 & \verb|remote| is \verb|D| and \verb|local| is wildcard \\ +3 & \verb|remote| is wildcard and \verb|local| is \verb|S| \\ +4 & \verb|tunl0| +\end{tabular} + +If tunnel exists, but it is not in \verb|UP| state, the tunnel is ignored. +Note, that if \verb|tunl0| is \verb|UP| it receives all the IPIP packets, +not acknowledged by more specific tunnels. +Be careful, it means that without carefully installed firewall rules +anyone on the Internet may inject to your network any packets with +source addresses indistinguishable from local ones. It is not so bad idea +to design tunnels in the way enforcing maximal route symmetry +and to enable reversed path filter (\verb|rp_filter| sysctl option) on +tunnel devices. + +\item In 2.2 you can monitor and debug tunnels with \verb|tcpdump|. +F.e.\ \verb|tcpdump| \verb|-i Cisco| \verb|-nvv| will dump packets, +which kernel output, via tunnel \verb|Cisco| and the packets received on it +from kernel viewpoint. + +\end{itemize} + + +\section{Linux and Cisco IOS tunnels.} + +Among another tunnels Cisco IOS supports IPIP and GRE. +Essentially, Cisco setup is subset of options, available for Linux. +Let us consider the simplest example: + +\begin{verbatim} +interface Tunnel0 + tunnel mode gre ip + tunnel source 10.10.14.1 + tunnel destination 10.10.13.2 +\end{verbatim} + + +This command set translates to: + +\begin{verbatim} + ip tunnel add Tunnel0 \ + mode gre \ + local 10.10.14.1 \ + remote 10.10.13.2 +\end{verbatim} + +Any questions? No questions. + +\section{Interaction IPIP tunnels and DVMRP.} + +DVMRP exploits IPIP tunnels to route multicasts via Internet. +\verb|mrouted| creates +IPIP tunnels listed in its configuration file automatically. +From kernel and user viewpoints there are no differences between +tunnels, created in this way, and tunnels created by \verb|ip tunnel|. +I.e.\ if \verb|mrouted| created some tunnel, it may be used to +route unicast packets, provided appropriate routes are added. +And vice versa, if administrator has already created a tunnel, +it will be reused by \verb|mrouted|, if it requests DVMRP +tunnel with the same local and remote addresses. + +Do not wonder, if your manually configured tunnel is +destroyed, when mrouted exits. + + +\section{Broadcast GRE ``tunnels''.} + +It is possible to set \verb|remote| for GRE tunnel to a multicast +address. Such tunnel becomes {\bf broadcast} tunnel (though word +tunnel is not quite appropriate in this case, it is rather virtual network). +\begin{verbatim} + ip tunnel add Universe local 193.233.7.65 \ + remote 224.66.66.66 ttl 16 + ip addr add 10.0.0.1/16 dev Universe + ip link set Universe up +\end{verbatim} +This tunnel is true broadcast network and broadcast packets are +sent to multicast group 224.66.66.66. By default such tunnel starts +to resolve both IP and IPv6 addresses via ARP/NDISC, so that +if multicast routing is supported in surrounding network, all GRE nodes +will find one another automatically and will form virtual Ethernet-like +broadcast network. If multicast routing does not work, it is unpleasant +but not fatal flaw. The tunnel becomes NBMA rather than broadcast network. +You may disable dynamic ARPing by: +\begin{verbatim} + echo 0 > /proc/sys/net/ipv4/neigh/Universe/mcast_solicit +\end{verbatim} +and to add required information to ARP tables manually: +\begin{verbatim} + ip neigh add 10.0.0.2 lladdr 128.6.190.2 dev Universe nud permanent +\end{verbatim} +In this case packets sent to 10.0.0.2 will be encapsulated in GRE +and sent to 128.6.190.2. It is possible to facilitate address resolution +using methods typical for another NBMA networks f.e.\ to start user +level \verb|arpd| daemon, which will maintain database of hosts attached +to GRE virtual network or ask for information +dedicated ARP or NHRP server. + + +Actually, such setup is the most natural for tunneling, +it is really flexible, scalable and easily managable, so that +it is strongly recommended to be used with GRE tunnels instead of ugly +hack with NBMA mode and \verb|onlink| modifier. Unfortunately, +by historical reasons broadcast mode is not supported by IPIP tunnels, +but this probably will change in future. + + + +\section{Traffic control issues.} + +Tunnels are devices, hence all the power of Linux traffic control +applies to them. The simplest (and the most useful in practice) +example is limiting tunnel bandwidth. The following command: +\begin{verbatim} + tc qdisc add dev tunl0 root tbf \ + rate 128Kbit burst 4K limit 10K +\end{verbatim} +will limit tunneled traffic to 128Kbit with maximal burst size of 4K +and queuing not more than 10K. + +However, you should remember, that tunnels are {\em virtual} devices +implemented in software and true queue management is impossible for them +just because they have no queues. Instead, it is better to create classes +on real physical interfaces and to map tunneled packets to them. +In general case of dynamic routing you should create such classes +on all outgoing interfaces, or, alternatively, +to use option \verb|dev DEV| to bind tunnel to a fixed physical device. +In the last case packets will be routed only via specified device +and you need to setup corresponding classes only on it. +Though you have to pay for this convenience, +if routing will change, your tunnel will fail. + +Suppose that CBQ class \verb|1:ABC| has been created on device \verb|eth0| +specially for tunnel \verb|Cisco| with endpoints \verb|S| and \verb|D|. +Now you can select IPIP packets with addresses \verb|S| and \verb|D| +with some classifier and map them to class \verb|1:ABC|. F.e.\ +it is easy to make with \verb|rsvp| classifier: +\begin{verbatim} + tc filter add dev eth0 pref 100 proto ip rsvp \ + session D ipproto ipip filter S \ + classid 1:ABC +\end{verbatim} + +If you want to make more detailed classification of sub-flows +transmitted via tunnel, you can build CBQ subtree, +rooted at \verb|1:ABC| and attach to subroot set of rules parsing +IPIP packets more deeply. + +\end{document} diff --git a/doc/nstat.sgml b/doc/nstat.sgml new file mode 100644 index 0000000..be9d8bc --- /dev/null +++ b/doc/nstat.sgml @@ -0,0 +1,110 @@ +<!doctype linuxdoc system> + +<article> + +<title>NSTAT, IFSTAT and RTACCT Utilities +<author>Alexey Kuznetosv, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/nstat/, <tt/ifstat/ and <tt/rtacct/ are simple tools helping +to monitor kernel snmp counters and network interface statistics. +</abstract> + +<p> These utilities are very similar, so that I describe +them simultaneously, using name <tt/Xstat/ in the places which apply +to all of them. + +<p>The format of the command is: + +<tscreen><verb> + Xstat [ OPTIONS ] [ PATTERN [ PATTERN ... ] ] +</verb></tscreen> + +<p> +<tt/PATTERN/ is shell style pattern, selecting identifier +of SNMP variables or interfaces to show. Variable is displayed +if one of patterns matches its name. If no patterns are given, +<tt/Xstat/ assumes that user wants to see all the variables. + +<p> <tt/OPTIONS/ is list of single letter options, using common unix +conventions. + +<itemize> +<item><tt/-h/ - show help page +<item><tt/-?/ - the same, of course +<item><tt/-v/, <tt/-V/ - print version of <tt/Xstat/ and exit +<item><tt/-z/ - dump zero counters too. By default they are not shown. +<item><tt/-a/ - dump absolute values of counters. By default <tt/Xstat/ + calculates increments since the previous use. +<item><tt/-s/ - do not update history, so that the next time you will + see counters including values accumulated to the moment + of this measurement too. +<item><tt/-n/ - do not display anything, only update history. +<item><tt/-r/ - reset history. +<item><tt/-d INTERVAL/ - <tt/Xstat/ is run in daemon mode collecting + statistics. <tt/INTERVAL/ is interval between measurements + in seconds. +<item><tt/-t INTERVAL/ - time interval to average rates. Default value + is 60 seconds. +<item><tt/-e/ - display extended information about errors (<tt/ifstat/ only). +</itemize> + +<p> +History is just dump saved in file <tt>/tmp/.Xstat.uUID</tt> +or in file given by environment variables <tt/NSTAT_HISTORY/, +<tt/IFSTAT_HISTORY/ and <tt/RTACCT_HISTORY/. +Each time when you use <tt/Xstat/ values there are updated. +If you use patterns, only the values which you _really_ see +are updated. If you want to skip an unintersting period, +use option <tt/-n/, or just output to <tt>/dev/null</tt>. + +<p> +<tt/Xstat/ understands when history is invalidated by system reboot +or source of information switched between different instances +of daemonic <tt/Xstat/ and kernel SNMP tables and does not +use invalid history. + +<p> Beware, <tt/Xstat/ will not produce sane output, +when many processes use it simultaneously. If several processes +under single user need this utility they should use environment +variables to put their history in safe places +or to use it with options <tt/-a -s/. + +<p> +Well, that's all. The utility is very simple, but nevertheless +very handy. + +<p> <bf/Output of XSTAT/ +<p> The first line of output is <tt/#/ followed by identifier +of source of information, it may be word <tt/kernel/, when <tt/Xstat/ +gets information from kernel or some dotted decimal number followed +by parameters, when it obtains information from running <tt/Xstat/ daemon. + +<p>In the case of <tt/nstat/ the rest of output consists of three columns: +SNMP MIB identifier, +its value (or increment since previous measurement) and average +rate of increase of the counter per second. <tt/ifstat/ outputs +interface name followed by pairs of counter and rate of its change. + +<p> <bf/Daemonic Xstat/ +<p> <tt/Xstat/ may be started as daemon by any user. This makes sense +to avoid wrapped counters and to obtain reasonable long counters +for large time. Also <tt/Xstat/ daemon calculates average rates. +For the first goal sampling interval (option <tt/-d/) may be large enough, +f.e. for gigabit rates byte counters overflow not more frequently than +each 40 seconds and you may select interval of 20 seconds. +From the other hand, when <tt/Xstat/ is used for estimating rates +interval should be less than averaging period (option <tt/-t/), otherwise +estimation loses in quality. + +Client <tt/Xstat/, before trying to get information from the kernel, +contacts daemon started by this user, then it tries system wide +daemon, which is supposed to be started by superuser. And only if +none of them replied it gets information from kernel. + +<p> <bf/Environment/ +<p> <tt/NSTAT_HISTORY/ - name of history file for <tt/nstat/. +<p> <tt/IFSTAT_HISTORY/ - name of history file for <tt/ifstat/. +<p> <tt/RTACCT_HISTORY/ - name of history file for <tt/rtacct/. + +</article> diff --git a/doc/preamble.tex b/doc/preamble.tex new file mode 100644 index 0000000..80ca508 --- /dev/null +++ b/doc/preamble.tex @@ -0,0 +1,26 @@ +\textwidth 6.0in +\textheight 8.5in + +\input SNAPSHOT + +\pagestyle{myheadings} +\markboth{\protect\TITLE}{} +\markright{{\protect\sc iproute2-ss\Draft}} + +% To print it in compact form: both sides on one sheet (psnup -2) +\evensidemargin=\oddsidemargin + +\newenvironment{NB}{\bgroup \vskip 1mm\leftskip 1cm \footnotesize \noindent NB. +}{\par\egroup \vskip 1mm} + +\def\threeonly{[2.3.15+ only] } + +\begin{document} + +\makeatletter +\renewcommand{\@oddhead}{{\protect\sc iproute2-ss\Draft} \hfill \protect\arabic{page}} +\makeatother +\let\oldthefootnote\thefootnote +\def\thefootnote{} +\footnotetext{Copyright \copyright~1999 A.N.Kuznetsov} + diff --git a/doc/rtstat.sgml b/doc/rtstat.sgml new file mode 100644 index 0000000..07391c3 --- /dev/null +++ b/doc/rtstat.sgml @@ -0,0 +1,52 @@ +<!doctype linuxdoc system> + +<article> + +<title>RTACCT Utility +<author>Robert Olsson +<date>some_negative_number, 20 Dec 2001 + +<p> +Here is some code for monitoring the route cache. For systems handling high +network load, servers, routers, firewalls etc the route cache and its garbage +collection is crucial. Linux has a solid implementation. + +<p> +The kernel patch (not required since linux-2.4.7) adds statistics counters +from route cache process into +/proc/net/rt_cache_stat. A companion user mode program presents the statistics +in a vmstat or iostat manner. The ratio between cache hits and misses gives +the flow length. + +<p> +Hopefully it can help understanding performance and DoS and other related +issues. + +<p> An URL where newer versions of this utility can be (probably) found +is ftp://robur.slu.se/pub/Linux/net-development/rt_cache_stat/ + + +<p><bf/Description/ + +<p>The format of the command is: + +<tscreen><verb> + rtstat [ OPTIONS ] +</verb></tscreen> + +<p> <tt/OPTIONS/ are: + +<itemize> + +<item><tt/-h/, <tt/-help/ - show help page and version of the utility. + +<item><tt/-i INTERVAL/ - interval between snapshots, default value is +2 seconds. + +<item><tt/-s NUMBER/ - whether to print header line. 0 inhibits header line, +1 prescribes to print it once and 2 (this is default setting) forces header +line each 20 lines. + +</itemize> + +</article> diff --git a/doc/ss.sgml b/doc/ss.sgml new file mode 100644 index 0000000..0b1b533 --- /dev/null +++ b/doc/ss.sgml @@ -0,0 +1,525 @@ +<!doctype linuxdoc system> + +<article> + +<title>SS Utility: Quick Intro +<author>Alexey Kuznetosv, <tt/kuznet@ms2.inr.ac.ru/ +<date>some_negative_number, 20 Sep 2001 +<abstract> +<tt/ss/ is one another utility to investigate sockets. +Functionally it is NOT better than <tt/netstat/ combined +with some perl/awk scripts and though it is surely faster +it is not enough to make it much better. :-) +So, stop reading this now and do not waste your time. +Well, certainly, it proposes some functionality, which current +netstat is still not able to do, but surely will soon. +</abstract> + +<sect>Why? + +<p> <tt>/proc</tt> interface is inadequate, unfortunately. +When amount of sockets is enough large, <tt/netstat/ or even +plain <tt>cat /proc/net/tcp/</tt> cause nothing but pains and curses. +In linux-2.4 the desease became worse: even if amount +of sockets is small reading <tt>/proc/net/tcp/</tt> is slow enough. + +This utility presents a new approach, which is supposed to scale +well. I am not going to describe technical details here and +will concentrate on description of the command. +The only important thing to say is that it is not so bad idea +to load module <tt/tcp_diag/, which can be found in directory +<tt/Modules/ of <tt/iproute2/. If you do not make this <tt/ss/ +will work, but it falls back to <tt>/proc</tt> and becomes slow +like <tt/netstat/, well, a bit faster yet (see section "Some numbers"). + +<sect>Old news + +<p> +In the simplest form <tt/ss/ is equivalent to netstat +with some small deviations. + +<itemize> +<item><tt/ss -t -a/ dumps all TCP sockets +<item><tt/ss -u -a/ dumps all UDP sockets +<item><tt/ss -w -a/ dumps all RAW sockets +<item><tt/ss -x -a/ dumps all UNIX sockets +</itemize> + +<p> +Option <tt/-o/ shows TCP timers state. +Option <tt/-e/ shows some extended information. +Etc. etc. etc. Seems, all the options of netstat related to sockets +are supported. Though not AX.25 and other bizarres. :-) +If someone wants, he can make support for decnet and ipx. +Some rudimentary support for them is already present in iproute2 libutils, +and I will be glad to see these new members. + +<p> +However, standard functionality is a bit different: + +<p> +The first: without option <tt/-a/ sockets in states +<tt/TIME-WAIT/ and <tt/SYN-RECV/ are skipped too. +It is more reasonable default, I think. + +<p> +The second: format of UNIX sockets is different. It coincides +with tcp/udp. Though standard kernel still does not allow to +see write/read queues and peer address of connected UNIX sockets, +the patch doing this exists. + +<p> +The third: default is to dump only TCP sockets, rather than all of the types. + +<p> +The next: by default it does not resolve numeric host addresses (like <tt/ip/)! +Resolving is enabled with option <tt/-r/. Service names, usually stored +in local files, are resolved by default. Also, if service database +does not contain references to a port, <tt/ss/ queries system +<tt/rpcbind/. RPC services are prefixed with <tt/rpc./ +Resolution of services may be suppressed with option <tt/-n/. + +<p> +It does not accept "long" options (I dislike them, sorry). +So, address family is given with family identifier following +option <tt/-f/ to be algined to iproute2 conventions. +Mostly, it is to allow option parser to parse +addresses correctly, but as side effect it really limits dumping +to sockets supporting only given family. Option <tt/-A/ followed +by list of socket tables to dump is also supported. +Logically, id of socket table is different of _address_ family, which is +another point of incompatibility. So, id is one of +<tt/all/, <tt/tcp/, <tt/udp/, +<tt/raw/, <tt/inet/, <tt/unix/, <tt/packet/, <tt/netlink/. See? +Well, <tt/inet/ is just abbreviation for <tt/tcp|udp|raw/ +and it is not difficult to guess that <tt/packet/ allows +to look at packet sockets. Actually, there are also some other abbreviations, +f.e. <tt/unix_dgram/ selects only datagram UNIX sockets. + +<p> +The next: well, I still do not know. :-) + + + + +<sect>Time to talk about new functionality. + +<p>It is builtin filtering of socket lists. + +<sect1> Filtering by state. + +<p> +<tt/ss/ allows to filter socket states, using keywords +<tt/state/ and <tt/exclude/, followed by some state +identifier. + +<p> +State identifier are standard TCP state names (not listed, +they are useless for you if you already do not know them) +or abbreviations: + +<itemize> +<item><tt/all/ - for all the states +<item><tt/bucket/ - for TCP minisockets (<tt/TIME-WAIT|SYN-RECV/) +<item><tt/big/ - all except for minisockets +<item><tt/connected/ - not closed and not listening +<item><tt/synchronized/ - connected and not <tt/SYN-SENT/ +</itemize> + +<p> + F.e. to dump all tcp sockets except <tt/SYN-RECV/: + +<tscreen><verb> + ss exclude SYN-RECV +</verb></tscreen> + +<p> + If neither <tt/state/ nor <tt/exclude/ directives + are present, + state filter defaults to <tt/all/ with option <tt/-a/ + or to <tt/all/, + excluding listening, syn-recv, time-wait and closed sockets. + +<sect1> Filtering by addresses and ports. + +<p> +Option list may contain address/port filter. +It is boolean expression which consists of boolean operation +<tt/or/, <tt/and/, <tt/not/ and predicates. +Actually, all the flavors of names for boolean operations are eaten: +<tt/&/, <tt/&&/, <tt/|/, <tt/||/, <tt/!/, but do not forget +about special sense given to these symbols by unix shells and escape +them correctly, when used from command line. + +<p> +Predicates may be of the folowing kinds: + +<itemize> +<item>A. Address/port match, where address is checked against mask + and port is either wildcard or exact. It is one of: + +<tscreen><verb> + dst prefix:port + src prefix:port + src unix:STRING + src link:protocol:ifindex + src nl:channel:pid +</verb></tscreen> + + Both prefix and port may be absent or replaced with <tt/*/, + which means wildcard. UNIX socket use more powerful scheme + matching to socket names by shell wildcards. Also, prefixes + unix: and link: may be omitted, if address family is evident + from context (with option <tt/-x/ or with <tt/-f unix/ + or with <tt/unix/ keyword) + +<p> + F.e. + +<tscreen><verb> + dst 10.0.0.1 + dst 10.0.0.1: + dst 10.0.0.1/32: + dst 10.0.0.1:* +</verb></tscreen> + are equivalent and mean socket connected to + any port on host 10.0.0.1 + +<tscreen><verb> + dst 10.0.0.0/24:22 +</verb></tscreen> + sockets connected to port 22 on network + 10.0.0.0...255. + +<p> + Note that port separated of address with colon, which creates + troubles with IPv6 addresses. Generally, we interpret the last + colon as splitting port. To allow to give IPv6 addresses, + trick like used in IPv6 HTTP URLs may be used: + +<tscreen><verb> + dst [::1] +</verb></tscreen> + are sockets connected to ::1 on any port + +<p> + Another way is <tt/dst ::1/128/. / helps to understand that + colon is part of IPv6 address. + +<p> + Now we can add another alias for <tt/dst 10.0.0.1/: + <tt/dst [10.0.0.1]/. :-) + +<p> Address may be a DNS name. In this case all the addresses are looked + up (in all the address families, if it is not limited by option <tt/-f/ + or special address prefix <tt/inet:/, <tt/inet6/) and resulting + expression is <tt/or/ over all of them. + +<item> B. Port expressions: +<tscreen><verb> + dport >= :1024 + dport != :22 + sport < :32000 +</verb></tscreen> + etc. + + All the relations: <tt/</, <tt/>/, <tt/=/, <tt/>=/, <tt/=/, <tt/==/, + <tt/!=/, <tt/eq/, <tt/ge/, <tt/lt/, <tt/ne/... + Use variant which you like more, but not forget to escape special + characters when typing them in command line. :-) + + Note that port number syntactically coincides to the case A! + You may even add an IP address, but it will not participate + incomparison, except for <tt/==/ and <tt/!=/, which are equivalent + to corresponding predicates of type A. F.e. +<p> +<tt/dst 10.0.0.1:22/ + is equivalent to <tt/dport eq 10.0.0.1:22/ + and + <tt/not dst 10.0.0.1:22/ is equivalent to + <tt/dport neq 10.0.0.1:22/ + +<item>C. Keyword <tt/autobound/. It matches to sockets bound automatically + on local system. + +</itemize> + + +<sect> Examples + +<p> +<itemize> +<item>1. List all the tcp sockets in state <tt/FIN-WAIT-1/ for our apache + to network 193.233.7/24 and look at their timers: + +<tscreen><verb> + ss -o state fin-wait-1 \( sport = :http or sport = :https \) \ + dst 193.233.7/24 +</verb></tscreen> + + Oops, forgot to say that missing logical operation is + equivalent to <tt/and/. + +<item> 2. Well, now look at the rest... + +<tscreen><verb> + ss -o excl fin-wait-1 + ss state fin-wait-1 \( sport neq :http and sport neq :https \) \ + or not dst 193.233.7/24 +</verb></tscreen> + + Note that we have to do _two_ calls of ss to do this. + State match is always anded to address/port match. + The reason for this is purely technical: ss does fast skip of + not matching states before parsing addresses and I consider the + ability to skip fastly gobs of time-wait and syn-recv sockets + as more important than logical generality. + +<item> 3. So, let's look at all our sockets using autobound ports: + +<tscreen><verb> + ss -a -A all autobound +</verb></tscreen> + + +<item> 4. And eventually find all the local processes connected + to local X servers: + +<tscreen><verb> + ss -xp dst "/tmp/.X11-unix/*" +</verb></tscreen> + + Pardon, this does not work with current kernel, patching is required. + But we still can look at server side: + +<tscreen><verb> + ss -x src "/tmp/.X11-unix/*" +</verb></tscreen> + +</itemize> + + +<sect> Returning to ground: real manual + +<p> +<sect1> Command arguments + +<p> General format of arguments to <tt/ss/ is: + +<tscreen><verb> + ss [ OPTIONS ] [ STATE-FILTER ] [ ADDRESS-FILTER ] +</verb></tscreen> + +<sect2><tt/OPTIONS/ +<p> <tt/OPTIONS/ is list of single letter options, using common unix +conventions. + +<itemize> +<item><tt/-h/ - show help page +<item><tt/-?/ - the same, of course +<item><tt/-v/, <tt/-V/ - print version of <tt/ss/ and exit +<item><tt/-s/ - print summary statistics. This option does not parse +socket lists obtaining summary from various sources. It is useful +when amount of sockets is so huge that parsing <tt>/proc/net/tcp</tt> +is painful. +<item><tt/-D FILE/ - do not display anything, just dump raw information +about TCP sockets to <tt/FILE/ after applying filters. If <tt/FILE/ is <tt/-/ +<tt/stdout/ is used. +<item><tt/-F FILE/ - read continuation of filter from <tt/FILE/. +Each line of <tt/FILE/ is interpreted like single command line option. +If <tt/FILE/ is <tt/-/ <tt/stdin/ is used. +<item><tt/-r/ - try to resolve numeric address/ports +<item><tt/-n/ - do not try to resolve ports +<item><tt/-o/ - show some optional information, f.e. TCP timers +<item><tt/-i/ - show some infomration specific to TCP (RTO, congestion +window, slow start threshould etc.) +<item><tt/-e/ - show even more optional information +<item><tt/-m/ - show extended information on memory used by the socket. +It is available only with <tt/tcp_diag/ enabled. +<item><tt/-p/ - show list of processes owning the socket +<item><tt/-f FAMILY/ - default address family used for parsing addresses. + Also this option limits listing to sockets supporting + given address family. Currently the following families + are supported: <tt/unix/, <tt/inet/, <tt/inet6/, <tt/link/, + <tt/netlink/. +<item><tt/-4/ - alias for <tt/-f inet/ +<item><tt/-6/ - alias for <tt/-f inet6/ +<item><tt/-0/ - alias for <tt/-f link/ +<item><tt/-A LIST-OF-TABLES/ - list of socket tables to dump, separated + by commas. The following identifiers are understood: + <tt/all/, <tt/inet/, <tt/tcp/, <tt/udp/, <tt/raw/, + <tt/unix/, <tt/packet/, <tt/netlink/, <tt/unix_dgram/, + <tt/unix_stream/, <tt/packet_raw/, <tt/packet_dgram/. +<item><tt/-x/ - alias for <tt/-A unix/ +<item><tt/-t/ - alias for <tt/-A tcp/ +<item><tt/-u/ - alias for <tt/-A udp/ +<item><tt/-w/ - alias for <tt/-A raw/ +<item><tt/-a/ - show sockets of all the states. By default sockets + in states <tt/LISTEN/, <tt/TIME-WAIT/, <tt/SYN_RECV/ + and <tt/CLOSE/ are skipped. +<item><tt/-l/ - show only sockets in state <tt/LISTEN/ +</itemize> + +<sect2><tt/STATE-FILTER/ + +<p><tt/STATE-FILTER/ allows to construct arbitrary set of +states to match. Its syntax is sequence of keywords <tt/state/ +and <tt/exclude/ followed by identifier of state. +Available identifiers are: + +<p> +<itemize> +<item> All standard TCP states: <tt/established/, <tt/syn-sent/, +<tt/syn-recv/, <tt/fin-wait-1/, <tt/fin-wait-2/, <tt/time-wait/, +<tt/closed/, <tt/close-wait/, <tt/last-ack/, <tt/listen/ and <tt/closing/. + +<item><tt/all/ - for all the states +<item><tt/connected/ - all the states except for <tt/listen/ and <tt/closed/ +<item><tt/synchronized/ - all the <tt/connected/ states except for +<tt/syn-sent/ +<item><tt/bucket/ - states, which are maintained as minisockets, i.e. +<tt/time-wait/ and <tt/syn-recv/. +<item><tt/big/ - opposite to <tt/bucket/ +</itemize> + +<sect2><tt/ADDRESS_FILTER/ + +<p><tt/ADDRESS_FILTER/ is boolean expression with operations <tt/and/, <tt/or/ +and <tt/not/, which can be abbreviated in C style f.e. as <tt/&/, +<tt/&&/. + +<p> +Predicates check socket addresses, both local and remote. +There are the following kinds of predicates: + +<itemize> +<item> <tt/dst ADDRESS_PATTERN/ - matches remote address and port +<item> <tt/src ADDRESS_PATTERN/ - matches local address and port +<item> <tt/dport RELOP PORT/ - compares remote port to a number +<item> <tt/sport RELOP PORT/ - compares local port to a number +<item> <tt/autobound/ - checks that socket is bound to an ephemeral + port +</itemize> + +<p><tt/RELOP/ is some of <tt/<=/, <tt/>=/, <tt/==/ etc. +To make this more convinient for use in unix shell, alphabetic +FORTRAN-like notations <tt/le/, <tt/gt/ etc. are accepted as well. + +<p>The format and semantics of <tt/ADDRESS_PATTERN/ depends on address +family. + +<itemize> +<item><tt/inet/ - <tt/ADDRESS_PATTERN/ consists of IP prefix, optionally +followed by colon and port. If prefix or port part is absent or replaced +with <tt/*/, this means wildcard match. +<item><tt/inet6/ - The same as <tt/inet/, only prefix refers to an IPv6 +address. Unlike <tt/inet/ colon becomes ambiguous, so that <tt/ss/ allows +to use scheme, like used in URLs, where address is suppounded with +<tt/[/ ... <tt/]/. +<item><tt/unix/ - <tt/ADDRESS_PATTERN/ is shell-style wildcard. +<item><tt/packet/ - format looks like <tt/inet/, only interface index +stays instead of port and link layer protocol id instead of address. +<item><tt/netlink/ - format looks like <tt/inet/, only socket pid +stays instead of port and netlink channel instead of address. +</itemize> + +<p><tt/PORT/ is syntactically <tt/ADDRESS_PATTERN/ with wildcard +address part. Certainly, it is undefined for UNIX sockets. + +<sect1> Environment variables + +<p> +<tt/ss/ allows to change source of information using various +environment variables: + +<p> +<itemize> +<item> <tt/PROC_SLABINFO/ to override <tt>/proc/slabinfo</tt> +<item> <tt/PROC_NET_TCP/ to override <tt>/proc/net/tcp</tt> +<item> <tt/PROC_NET_UDP/ to override <tt>/proc/net/udp</tt> +<item> etc. +</itemize> + +<p> +Variable <tt/PROC_ROOT/ allows to change root of all the <tt>/proc/</tt> +hierarchy. + +<p> +Variable <tt/TCPDIAG_FILE/ prescribes to open a file instead of +requesting kernel to dump information about TCP sockets. + + +<p> This option is used mainly to investigate bug reports, +when dumps of files usually found in <tt>/proc/</tt> are recevied +by e-mail. + +<sect1> Output format + +<p>Six columns. The first is <tt/Netid/, it denotes socket type and +transport protocol, when it is ambiguous: <tt/tcp/, <tt/udp/, <tt/raw/, +<tt/u_str/ is abbreviation for <tt/unix_stream/, <tt/u_dgr/ for UNIX +datagram sockets, <tt/nl/ for netlink, <tt/p_raw/ and <tt/p_dgr/ for +raw and datagram packet sockets. This column is optional, it will +be hidden, if filter selects an unique netid. + +<p> +The second column is <tt/State/. Socket state is displayed here. +The names are standard TCP names, except for <tt/UNCONN/, which +cannot happen for TCP, but normal for not connected sockets +of another types. Again, this column can be hidden. + +<p> +Then two columns (<tt/Recv-Q/ and <tt/Send-Q/) showing amount of data +queued for receive and transmit. + +<p> +And the last two columns display local address and port of the socket +and its peer address, if the socket is connected. + +<p> +If options <tt/-o/, <tt/-e/ or <tt/-p/ were given, options are +displayed not in fixed positions but separated by spaces pairs: +<tt/option:value/. If value is not a single number, it is presented +as list of values, enclosed to <tt/(/ ... <tt/)/ and separated with +commas. F.e. + +<tscreen><verb> + timer:(keepalive,111min,0) +</verb></tscreen> +is typical format for TCP timer (option <tt/-o/). + +<tscreen><verb> + users:((X,113,3)) +</verb></tscreen> +is typical for list of users (option <tt/-p/). + + +<sect>Some numbers + +<p> +Well, let us use <tt/pidentd/ and a tool <tt/ibench/ to measure +its performance. It is 30 requests per second here. Nothing to test, +it is too slow. OK, let us patch pidentd with patch from directory +Patches. After this it handles about 4300 requests per second +and becomes handy tool to pollute socket tables with lots of timewait +buckets. + +<p> +So, each test starts from pollution tables with 30000 sockets +and then doing full dump of the table piped to wc and measuring +timings with time: + +<p>Results: + +<itemize> +<item> <tt/netstat -at/ - 15.6 seconds +<item> <tt/ss -atr/, but without <tt/tcp_diag/ - 5.4 seconds +<item> <tt/ss -atr/ with <tt/tcp_diag/ - 0.47 seconds +</itemize> + +No comments. Though one comment is necessary, most of time +without <tt/tcp_diag/ is wasted inside kernel with completely +blocked networking. More than 10 seconds, yes. <tt/tcp_diag/ +does the same work for 100 milliseconds of system time. + +</article> diff --git a/etc/iproute2/rt_dsfield b/etc/iproute2/rt_dsfield new file mode 100644 index 0000000..2b36e49 --- /dev/null +++ b/etc/iproute2/rt_dsfield @@ -0,0 +1,15 @@ +#0x10 lowdelay +#0x08 throughput +#0x04 reliability + +# This value overlap with ECT, do not use it! +#0x02 mincost + +# These values seems do not want to die, Cisco likes them by a strange reason. +#0x20 priority +#0x40 immediate +#0x60 flash +#0x80 flash-override +#0xa0 critical +#0xc0 internet +#0xe0 network diff --git a/etc/iproute2/rt_dsfield.rt_config b/etc/iproute2/rt_dsfield.rt_config new file mode 100644 index 0000000..110061a --- /dev/null +++ b/etc/iproute2/rt_dsfield.rt_config @@ -0,0 +1,13 @@ +0x10 lowdelay +0x08 throughput +0x04 reliability +# This value overlap with ECT, do not use it! +0x02 mincost +# These values seems do not want to die, Cisco likes them by a strange reason. +0x20 priority +0x40 immediate +0x60 flash +0x80 flash-override +0xa0 critical +0xc0 internet +0xe0 network diff --git a/etc/iproute2/rt_protos b/etc/iproute2/rt_protos new file mode 100644 index 0000000..2569edf --- /dev/null +++ b/etc/iproute2/rt_protos @@ -0,0 +1,26 @@ +# +# Reserved protocols. +# +#0 unspec +#1 redirect +#2 kernel +#3 boot +#4 static +#8 gated +#9 ra +#10 mrt +#11 zebra +#12 bird + +# +# Used by me for gated +# +#254 gated/aggr +#253 gated/bgp +#252 gated/ospf +#251 gated/ospfase +#250 gated/rip +#249 gated/static +#248 gated/conn +#247 gated/inet +#246 gated/default diff --git a/etc/iproute2/rt_protos.rt_config b/etc/iproute2/rt_protos.rt_config new file mode 100644 index 0000000..8c985d7 --- /dev/null +++ b/etc/iproute2/rt_protos.rt_config @@ -0,0 +1,25 @@ +# +# Reserved protocols. +# +0 unspec +1 redirect +2 kernel +3 boot +4 static +8 gated +9 ra +10 mrt +11 zebra +12 bird +# +# Used by me for gated +# +254 gated/aggr +253 gated/bgp +252 gated/ospf +251 gated/ospfase +250 gated/rip +249 gated/static +248 gated/conn +247 gated/inet +246 gated/default diff --git a/etc/iproute2/rt_realms b/etc/iproute2/rt_realms new file mode 100644 index 0000000..332179d --- /dev/null +++ b/etc/iproute2/rt_realms @@ -0,0 +1,13 @@ +# +# reserved values +# +#0 cosmos +# +# local +# +#1 inr.ac +#2 inr.ruhep +#3 freenet +#4 radio-msu +#5 russia +#6 internet diff --git a/etc/iproute2/rt_realms.rt_config b/etc/iproute2/rt_realms.rt_config new file mode 100644 index 0000000..eedd76d --- /dev/null +++ b/etc/iproute2/rt_realms.rt_config @@ -0,0 +1,13 @@ +# +# reserved values +# +0 cosmos +# +# local +# +#1 inr.ac +#2 inr.ruhep +#3 freenet +#4 radio-msu +#5 russia +#6 internet diff --git a/etc/iproute2/rt_scopes b/etc/iproute2/rt_scopes new file mode 100644 index 0000000..36fbc01 --- /dev/null +++ b/etc/iproute2/rt_scopes @@ -0,0 +1,12 @@ +# +# reserved values +# +#0 global +#255 nowhere +#254 host +#253 link + +# +# pseudo-reserved +# +#200 site diff --git a/etc/iproute2/rt_scopes.rt_config b/etc/iproute2/rt_scopes.rt_config new file mode 100644 index 0000000..8514bc1 --- /dev/null +++ b/etc/iproute2/rt_scopes.rt_config @@ -0,0 +1,11 @@ +# +# reserved values +# +0 global +255 nowhere +254 host +253 link +# +# pseudo-reserved +# +200 site diff --git a/etc/iproute2/rt_tables b/etc/iproute2/rt_tables new file mode 100644 index 0000000..558716b --- /dev/null +++ b/etc/iproute2/rt_tables @@ -0,0 +1,11 @@ +# +# reserved values +# +#255 local +#254 main +#253 default +#0 unspec +# +# local +# +#1 inr.ruhep diff --git a/etc/iproute2/rt_tables.rt_config b/etc/iproute2/rt_tables.rt_config new file mode 100644 index 0000000..541abfd --- /dev/null +++ b/etc/iproute2/rt_tables.rt_config @@ -0,0 +1,11 @@ +# +# reserved values +# +255 local +254 main +253 default +0 unspec +# +# local +# +#1 inr.ruhep diff --git a/examples/SYN-DoS.rate.limit b/examples/SYN-DoS.rate.limit new file mode 100644 index 0000000..8766b67 --- /dev/null +++ b/examples/SYN-DoS.rate.limit @@ -0,0 +1,49 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# this script shows how one can rate limit incoming SYNs +# Useful for TCP-SYN attack protection. You can use +# IPchains to have more powerful additions to the SYN (eg +# in addition the subnet) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +# +# tag all incoming SYN packets through $INDEV as mark value 1 +############################################################ +$IPCHAINS -A input -i $INDEV -y -m 1 +############################################################ +# +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ + +# +# +# SYN packets are 40 bytes (320 bits) so three SYNs equals +# 960 bits (approximately 1kbit); so we rate limit below +# the incoming SYNs to 3/sec (not very sueful really; but +#serves to show the point - JHS +############################################################ +$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \ +police rate 1kbit burst 40 mtu 9k drop flowid :1 +############################################################ + + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/cbqinit.eth1 b/examples/cbqinit.eth1 new file mode 100755 index 0000000..226ec1c --- /dev/null +++ b/examples/cbqinit.eth1 @@ -0,0 +1,76 @@ +#! /bin/sh + +TC=/home/root/tc +IP=/home/root/ip +DEVICE=eth1 +BANDWIDTH="bandwidth 10Mbit" + +# Attach CBQ on $DEVICE. It will have handle 1:. +# $BANDWIDTH is real $DEVICE bandwidth (10Mbit). +# avpkt is average packet size. +# mpu is minimal packet size. + +$TC qdisc add dev $DEVICE root handle 1: cbq \ +$BANDWIDTH avpkt 1000 mpu 64 + +# Create root class with classid 1:1. This step is not necessary. +# bandwidth is the same as on CBQ itself. +# rate == all the bandwidth +# allot is MTU + MAC header +# maxburst measure allowed class burstiness (please,read S.Floyd and VJ papers) +# est 1sec 8sec means, that kernel will evaluate average rate +# on this class with period 1sec and time constant 8sec. +# This rate is viewed with "tc -s class ls dev $DEVICE" + +$TC class add dev $DEVICE parent 1:0 classid :1 est 1sec 8sec cbq \ +$BANDWIDTH rate 10Mbit allot 1514 maxburst 50 avpkt 1000 + +# Bulk. +# New parameters are: +# weight, which is set to be proportional to +# "rate". It is not necessary, weight=1 will work as well. +# defmap and split say that best effort ttraffic, not classfied +# by another means will fall to this class. + +$TC class add dev $DEVICE parent 1:1 classid :2 est 1sec 8sec cbq \ +$BANDWIDTH rate 4Mbit allot 1514 weight 500Kbit \ +prio 6 maxburst 50 avpkt 1000 split 1:0 defmap ff3d + +# OPTIONAL. +# Attach "sfq" qdisc to this class, quantum is MTU, perturb +# gives period of hash function perturbation in seconds. +# +$TC qdisc add dev $DEVICE parent 1:2 sfq quantum 1514b perturb 15 + +# Interactive-burst class + +$TC class add dev $DEVICE parent 1:1 classid :3 est 2sec 16sec cbq \ +$BANDWIDTH rate 1Mbit allot 1514 weight 100Kbit \ +prio 2 maxburst 100 avpkt 1000 split 1:0 defmap c0 + +$TC qdisc add dev $DEVICE parent 1:3 sfq quantum 1514b perturb 15 + +# Background. + +$TC class add dev $DEVICE parent 1:1 classid :4 est 1sec 8sec cbq \ + $BANDWIDTH rate 100Kbit allot 1514 weight 10Mbit \ + prio 7 maxburst 10 avpkt 1000 split 1:0 defmap 2 + +$TC qdisc add dev $DEVICE parent 1:4 sfq quantum 1514b perturb 15 + +# Realtime class for RSVP + +$TC class add dev $DEVICE parent 1:1 classid 1:7FFE cbq \ +rate 5Mbit $BANDWIDTH allot 1514b avpkt 1000 \ +maxburst 20 + +# Reclassified realtime traffic +# +# New element: split is not 1:0, but 1:7FFE. It means, +# that only real-time packets, which violated policing filters +# or exceeded reshaping buffers will fall to it. + +$TC class add dev $DEVICE parent 1:7FFE classid 1:7FFF est 4sec 32sec cbq \ +rate 1Mbit $BANDWIDTH allot 1514b avpkt 1000 weight 10Kbit \ +prio 6 maxburst 10 split 1:7FFE defmap ffff + diff --git a/examples/dhcp-client-script b/examples/dhcp-client-script new file mode 100755 index 0000000..7207b57 --- /dev/null +++ b/examples/dhcp-client-script @@ -0,0 +1,446 @@ +#!/bin/bash +# +# dhclient-script for Linux. +# +# 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> +# +# Probably, I did not understand, what this funny feature as "alias" +# means exactly. For now I suppose, that it is a static address, which +# we should install and preserve. +# + +exec >> /tmp/DHS.log 2>&1 + +echo dhc-script $* reason=$reason +set | grep "^\(old_\|new_\|check_\)" + +LOG () { + echo LOG $* ; +} + +# convert 8bit mask to length +# arg: $1 = mask +# +Mask8ToLen() { + local l=0; + + while [ $l -le 7 ]; do + if [ $[ ( 1 << $l ) + $1 ] -eq 256 ]; then + return $[ 8 - $l ] + fi + l=$[ $l + 1 ] + done + return 0; +} + +# convert inet dotted quad mask to length +# arg: $1 = dotquad mask +# +MaskToLen() { + local masklen=0 + local mask8=$1 + + case $1 in + 0.0.0.0) + return 0; + ;; + 255.*.0.0) + masklen=8 + mask8=${mask8#255.} + mask8=${mask8%.0.0} + ;; + 255.255.*.0) + masklen=16 + mask8=${mask8#255.255.} + mask8=${mask8%.0} + ;; + 255.255.255.*) + masklen=24 + mask8=${mask8#255.255.255.} + ;; + *) + return 255 + ;; + esac + Mask8ToLen $mask8 + return $[ $? + $masklen ] +} + +# calculate ABC "natural" mask +# arg: $1 = dotquad address +# +ABCMask () { + local class; + + class=${1%%.*} + + if [ "$1" = "255.255.255.255" ]; then + echo $1 + elif [ "$1" = "0.0.0.0" ]; then + echo $1 + elif [ $class -ge 224 ]; then + echo 240.0.0.0 + elif [ $class -ge 192 ]; then + echo 255.255.255.0 + elif [ $class -ge 128 ]; then + echo 255.255.0.0 + else + echo 255.0.0.0 + fi +} + +# calculate ABC "natural" mask length +# arg: $1 = dotquad address +# +ABCMaskLen () { + local class; + + class=${1%%.*} + + if [ "$1" = "255.255.255.255" ]; then + return 32 + elif [ "$1" = "0.0.0.0" ]; then + return 0 + elif [ $class -ge 224 ]; then + return 4; + elif [ $class -ge 192 ]; then + return 24; + elif [ $class -ge 128 ]; then + return 16; + else + return 8; + fi +} + +# Delete IP address +# args: $1 = interface +# $2 = address +# $3 = mask +# $4 = broadcast +# $5 = label +# +DelINETAddr () { + local masklen=32 + local addrid=$1 + + LOG DelINETAddr $* + + if [ "$5" ]; then + addrid=$addrid:$5 + fi + LOG ifconfig $addrid down + ifconfig $addrid down +} + +# Add IP address +# args: $1 = interface +# $2 = address +# $3 = mask +# $4 = broadcast +# $5 = label +# +AddINETAddr () { + local mask_arg + local brd_arg + local addrid=$1 + + LOG AddINETAddr $* + + if [ "$5" ]; then + addrid=$addrid:$5 + fi + if [ "$3" ]; then + mask_arg="netmask $3" + fi + if [ "$4" ]; then + brd_arg="broadcast $4" + fi + + LOG ifconfig $addrid $2 $mask_arg $brd_arg up + ifconfig $addrid $2 $mask_arg $brd_arg up +} + +# Add default routes +# args: $1 = routers list +# +AddDefaultRoutes() { + local router + + if [ "$1" ]; then + LOG AddDefaultRoutes $* + for router in $1; do + LOG route add default gw $router + route add default gw $router + done ; + fi +} + +# Delete default routes +# args: $1 = routers list +# +DelDefaultRoutes() { + local router + + if [ "$1" ]; then + LOG DelDefaultRoutes $* + + for router in $1; do + LOG route del default gw $router + route del default gw $router + done + fi +} + +# ping a host +# args: $1 = dotquad address of the host +# +PingNode() { + LOG PingNode $* + if ping -q -c 1 -w 2 $1 ; then + return 0; + fi + return 1; +} + +# Check (and add route, if alive) default routers +# args: $1 = routers list +# returns: 0 if at least one router is alive. +# +CheckRouterList() { + local router + local succeed=1 + + LOG CheckRouterList $* + + for router in $1; do + if PingNode $router ; then + succeed=0 + route add default gw $router + fi + done + return $succeed +} + +# Delete/create static routes. +# args: $1 = operation (del/add) +# $2 = routes list in format "dst1 nexthop1 dst2 ..." +# +# BEWARE: this feature of DHCP is obsolete, because does not +# support subnetting. +# +X-StaticRouteList() { + local op=$1 + local lst="$2" + local masklen + + LOG X-StaticRouteList $* + + if [ "$lst" ]; then + set $lst + while [ $# -gt 1 ]; do + route $op -net $1 netmask `ABCMask "$1"` gw $2 + shift; shift; + done + fi +} + +# Create static routes. +# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..." +# +AddStaticRouteList() { + LOG AddStaticRouteList $* + X-StaticRouteList add "$1" +} + +# Delete static routes. +# arg: $1 = routes list in format "dst1 nexthop1 dst2 ..." +# +DelStaticRouteList() { + LOG DelStaticRouteList $* + X-StaticRouteList del "$1" +} + +# Broadcast unsolicited ARP to update neighbours' caches. +# args: $1 = interface +# $2 = address +# +UnsolicitedARP() { + if [ -f /sbin/arping ]; then + /sbin/arping -A -c 1 -I "$1" "$2" & + (sleep 2 ; /sbin/arping -U -c 1 -I "$1" "$2" ) & + fi +} + +# Duplicate address detection. +# args: $1 = interface +# $2 = test address +# returns: 0, if DAD succeeded. +DAD() { + if [ -f /sbin/arping ]; then + /sbin/arping -c 2 -w 3 -D -I "$1" "$2" + return $? + fi + return 0 +} + + +# Setup resolver. +# args: NO +# domain and nameserver list are passed in global variables. +# +# NOTE: we try to be careful and not to break user supplied resolv.conf. +# The script mangles it, only if it has dhcp magic signature. +# +UpdateDNS() { + local nameserver + local idstring="#### Generated by DHCPCD" + + LOG UpdateDNS $* + + if [ "$new_domain_name" = "" -a "$new_domain_name_servers" = "" ]; then + return 0; + fi + + echo $idstring > /etc/resolv.conf.dhcp + if [ "$new_domain_name" ]; then + echo search $new_domain_name >> /etc/resolv.conf.dhcp + fi + echo options ndots:1 >> /etc/resolv.conf.dhcp + + if [ "$new_domain_name_servers" ]; then + for nameserver in $new_domain_name_servers; do + echo nameserver $nameserver >> /etc/resolv.conf.dhcp + done + else + echo nameserver 127.0.0.1 >> /etc/resolv.conf.dhcp + fi + + if [ -f /etc/resolv.conf ]; then + if [ "`head -1 /etc/resolv.conf`" != "$idstring" ]; then + return 0 + fi + if [ "$old_domain_name" = "$new_domain_name" -a + "$new_domain_name_servers" = "$old_domain_name_servers" ]; then + return 0 + fi + fi + mv /etc/resolv.conf.dhcp /etc/resolv.conf +} + +case $reason in +NBI) + exit 1 + ;; + +MEDIUM) + exit 0 + ;; + +PREINIT) + ifconfig $interface:dhcp down + ifconfig $interface:dhcp1 down + if [ -d /proc/sys/net/ipv4/conf/$interface ]; then + ifconfig $interface:dhcp 10.10.10.10 netmask 255.255.255.255 + ifconfig $interface:dhcp down + if [ -d /proc/sys/net/ipv4/conf/$interface ]; then + LOG The interface $interface already configured. + fi + fi + ifconfig $interface:dhcp up + exit 0 + ;; + +ARPSEND) + exit 0 + ;; + +ARPCHECK) + if DAD "$interface" "$check_ip_address" ; then + exit 0 + fi + exit 1 + ;; + +BOUND|RENEW|REBIND|REBOOT) + if [ "$old_ip_address" -a "$alias_ip_address" -a \ + "$alias_ip_address" != "$old_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + if [ "$old_ip_address" -a "$old_ip_address" != "$new_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + fi + if [ "$old_ip_address" = "" -o "$old_ip_address" != "$new_ip_address" -o \ + "$reason" = "BOUND" -o "$reason" = "REBOOT" ]; then + AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + AddStaticRouteList "$new_static_routes" + AddDefaultRoutes "$new_routers" + UnsolicitedARP "$interface" "$new_ip_address" + fi + if [ "$new_ip_address" != "$alias_ip_address" -a "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + UpdateDNS + exit 0 + ;; + +EXPIRE|FAIL) + if [ "$alias_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + if [ "$old_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + fi + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 0 + ;; + +TIMEOUT) + if [ "$alias_ip_address" ]; then + DelINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi +# Seems, <null address> means, that no more old leases found. +# Or does it mean bug in dhcpcd? 8) Fail for now. + if [ "$new_ip_address" = "<null address>" ]; then + if [ "$old_ip_address" ]; then + DelINETAddr "$interface" "$old_ip_address" "$old_subnet_mask" "$old_broadcast_address" dhcp + fi + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 1 + fi + if DAD "$interface" "$new_ip_address" ; then + AddINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + UnsolicitedARP "$interface" "$new_ip_address" + if [ "$alias_ip_address" -a "$alias_ip_address" != "$new_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + UnsolicitedARP "$interface" "$alias_ip_address" + fi + if CheckRouterList "$new_routers" ; then + AddStaticRouteList "$new_static_routes" + UpdateDNS + exit 0 + fi + fi + DelINETAddr "$interface" "$new_ip_address" "$new_subnet_mask" "$new_broadcast_address" dhcp + DelDefaultRoutes "$old_routers" + DelStaticRouteList "$old_static_routes" + if [ "$alias_ip_address" ]; then + AddINETAddr "$interface" "$alias_ip_address" "$alias_subnet_mask" "$alias_broadcast_address" dhcp1 + fi + exit 1 + ;; +esac + +exit 0 diff --git a/examples/diffserv/Edge1 b/examples/diffserv/Edge1 new file mode 100644 index 0000000..4ddffdd --- /dev/null +++ b/examples/diffserv/Edge1 @@ -0,0 +1,68 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script just tags on the ingress interfac using Ipchains +# the result is used for fast classification and re-marking +# on the egress interface +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +# +# tag all incoming packets from host 10.2.0.24 to value 1 +# tag all incoming packets from host 10.2.0.3 to value 2 +# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3 +#These values are used in the egress +# +############################################################ +$IPCHAINS -A input -s 10.2.0.4/24 -m 3 +$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1 +$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2 + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 set_tc_index +# +# values of the DSCP to change depending on the class +# +#becomes EF +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0xb8 +#becomes AF11 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x28 +#becomes AF21 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x48 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent 1:0 + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 diff --git a/examples/diffserv/Edge2 b/examples/diffserv/Edge2 new file mode 100644 index 0000000..2f78da2 --- /dev/null +++ b/examples/diffserv/Edge2 @@ -0,0 +1,87 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script tags the fwmark on the ingress interface using IPchains +# the result is used first for policing on the Ingress interface then +# for fast classification and re-marking +# on the egress interface +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +# +# tag all incoming packets from host 10.2.0.24 to value 1 +# tag all incoming packets from host 10.2.0.3 to value 2 +# tag the rest of incoming packets from subnet 10.2.0.0/24 to value 3 +#These values are used in the egress +############################################################ +$IPCHAINS -A input -s 10.2.0.0/24 -m 3 +$IPCHAINS -A input -i $INDEV -s 10.2.0.24 -m 1 +$IPCHAINS -A input -i $INDEV -s 10.2.0.3 -m 2 +############################################################ +# +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ + +# +# attach a fw classifier to the ingress which polices anything marked +# by ipchains to tag value 3 (The rest of the subnet packets -- not +# tag 1 or 2) to not go beyond 1.5Mbps +# Allow up to at least 60 packets to burst (assuming maximum packet +# size of # 1.5 KB) in the long run and upto about 6 packets in the +# shot run + +############################################################ +$TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 3 fw \ +police rate 1500kbit burst 90k mtu 9k drop flowid :1 +############################################################ + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0xb8 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x28 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x48 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 1 fw classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 2 fw classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 4 handle 3 fw classid 1:3 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $DEV ingress diff --git a/examples/diffserv/Edge31-ca-u32 b/examples/diffserv/Edge31-ca-u32 new file mode 100644 index 0000000..25e6c0b --- /dev/null +++ b/examples/diffserv/Edge31-ca-u32 @@ -0,0 +1,170 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color aware mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +# The colors are defined using the Diffserv Fields +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/usr/src/iproute2-current +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +INDEV=eth0 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=1000kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +############################################################ +# +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +# Create u32 filters +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1: u32 \ +divisor 1 +############################################################ + +# The meters: Note that we have shared meters in this case as identified +# by the index parameter +meter1=" police index 1 rate $CIR1 burst $CBS1 " +meter2=" police index 2 rate $CIR2 burst $CBS1 " +meter3=" police index 3 rate $CIR2 burst $CBS2 " +meter4=" police index 4 rate $CIR1 burst $CBS2 " +meter5=" police index 5 rate $CIR1 burst $CBS2 " + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE + +# *********************** AF41 *************************** +#AF41 (DSCP 0x22) is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS +#policer 1 is used. +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip tos 0x88 0xfc \ +$meter1 \ +continue flowid :1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +# tcindex value of 2 +# policer 2 is used +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x88 0xfc \ +$meter2 \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x88 0xfc \ +$meter3 \ +drop flowid :3 +# + +# *********************** AF42 *************************** +#AF42 (DSCP 0x24) from is passed on with a tcindex value 2 +#if it doesnt exceed its CIR/CBS +#policer 2 is used. Note that this is shared with the AF41 +# +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x90 0xfc \ +$meter2 \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x90 0xfc \ +$meter3 \ +drop flowid :3 +# +# *********************** AF43 *************************** +# +#AF43 (DSCP 0x26) from is passed on with a tcindex value 3 +#if it doesnt exceed its CIR/CBS +#policer 3 is used. Note that this is shared with the AF41 and AF42 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x98 0xfc \ +$meter3 \ +drop flowid :3 +# +# *********************** BE *************************** +# +# Anything else (not from the AF4*) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# Note that the BE class is also used by the AF4* in the worst +# case +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \ +match ip src 0/0\ +$meter4 \ +drop flowid :4 + +######################## Egress side ######################## + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge31-cb-chains b/examples/diffserv/Edge31-cb-chains new file mode 100644 index 0000000..d7faae9 --- /dev/null +++ b/examples/diffserv/Edge31-cb-chains @@ -0,0 +1,132 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script fwmark tags(IPchains) based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with no PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=1000kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +meter1="police rate $CIR1 burst $CBS1 " +meter2="police rate $CIR1 burst $CBS2 " +meter3="police rate $CIR2 burst $CBS1 " +meter4="police rate $CIR2 burst $CBS2 " +meter5="police rate $CIR2 burst $CBS2 " +# +# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1 +# tag all incoming packets from any other subnet to fw tag 2 +############################################################ +$IPCHAINS -A input -i $INDEV -s 0/0 -m 2 +$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1 +# +############################################################ +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +############################################################ +# +# anything with fw tag of 1 is passed on with a tcindex value 1 +#if it doesnt exceed its allocated rate (CIR/CBS) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \ +$meter1 \ +continue flowid 4:1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \ +$meter2 \ +continue flowid 4:2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \ +$meter3 \ +drop flowid 4:3 +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 2 fw \ +$meter5 \ +drop flowid 4:4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping (using tcindex; could easily have +# replaced it with the fw classifier instead) +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-ca-u32 b/examples/diffserv/Edge32-ca-u32 new file mode 100644 index 0000000..edf21e4 --- /dev/null +++ b/examples/diffserv/Edge32-ca-u32 @@ -0,0 +1,198 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color aware mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.2) +# +# The colors are defined using the Diffserv Fields +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1000kbit +CIR2=500kbit +# the PIR is what is in excess of the CIR +PIR1=1000kbit +PIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k +#the EBS is about 20 max sized packets +EBS1=30k +EBS2=30k + +# The meters: Note that we have shared meters in this case as identified +# by the index parameter +meter1=" police index 1 rate $CIR1 burst $CBS1 " +meter1a=" police index 2 rate $PIR1 burst $EBS1 " +meter2=" police index 3 rate $CIR2 burst $CBS1 " +meter2a=" police index 4 rate $PIR2 burst $EBS1 " +meter3=" police index 5 rate $CIR2 burst $CBS2 " +meter3a=" police index 6 rate $PIR2 burst $EBS2 " +meter4=" police index 7 rate $CIR1 burst $CBS2 " + +############################################################ +# +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +# *********************** AF41 *************************** +#AF41 (DSCP 0x22) from is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 1 is used. +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \ +match ip tos 0x88 0xfc \ +$meter1 \ +continue flowid :1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \ +match ip tos 0x88 0xfc \ +$meter1a \ +continue flowid :1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +# tcindex value of 2 +# policer 2 is used +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \ +match ip tos 0x88 0xfc \ +$meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip tos 0x88 0xfc \ +$meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip tos 0x88 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip tos 0x88 0xfc \ +$meter3a \ +drop flowid :3 +# +# *********************** AF42 *************************** +#AF42 (DSCP 0x24) from is passed on with a tcindex value 2 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 2 is used. Note that this is shared with the AF41 +# +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 8 u32 \ +match ip tos 0x90 0xfc \ +$meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 9 u32 \ +match ip tos 0x90 0xfc \ +$meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 (policer 3) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 10 u32 \ +match ip tos 0x90 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 11 u32 \ +match ip tos 0x90 0xfc \ +$meter3a \ +drop flowid :3 + +# +# *********************** AF43 *************************** +# +#AF43 (DSCP 0x26) from is passed on with a tcindex value 3 +#if it doesnt exceed its CIR/CBS + PIR/EBS +#policer 3 is used. Note that this is shared with the AF41 and AF42 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 13 u32 \ +match ip tos 0x98 0xfc \ +$meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 14 u32 \ +match ip tos 0x98 0xfc \ +$meter3a \ +drop flowid :3 +# +## *********************** BE *************************** +## +## Anything else (not from the AF4*) gets discarded if it +## exceeds 1Mbps and by default goes to BE if it doesnt +## Note that the BE class is also used by the AF4* in the worst +## case +## +$TC filter add dev $INDEV parent ffff: protocol ip prio 16 u32 \ +match ip src 0/0\ +$meter4 \ +drop flowid :4 + +######################## Egress side ######################## + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-cb-chains b/examples/diffserv/Edge32-cb-chains new file mode 100644 index 0000000..804fad1 --- /dev/null +++ b/examples/diffserv/Edge32-cb-chains @@ -0,0 +1,144 @@ +#! /bin/sh -x +# +# sample script on using the ingress capabilities +# This script fwmark tags(IPchains) based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with no PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.1) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +IPCHAINS=/root/DS-6-beta/ipchains-1.3.9/ipchains +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1500kbit +CIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k + +meter1="police rate $CIR1 burst $CBS1 " +meter1a="police rate $CIR2 burst $CBS1 " +meter2="police rate $CIR1 burst $CBS2 " +meter2a="police rate $CIR2 burst $CBS2 " +meter3="police rate $CIR2 burst $CBS1 " +meter3a="police rate $CIR2 burst $CBS1 " +meter4="police rate $CIR2 burst $CBS2 " +meter5="police rate $CIR1 burst $CBS2 " +# +# tag the rest of incoming packets from subnet 10.2.0.0/24 to fw value 1 +# tag all incoming packets from any other subnet to fw tag 2 +############################################################ +$IPCHAINS -A input -i $INDEV -s 0/0 -m 2 +$IPCHAINS -A input -i $INDEV -s 10.2.0.0/24 -m 1 +# +############################################################ +# install the ingress qdisc on the ingress interface +$TC qdisc add dev $INDEV handle ffff: ingress +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +############################################################ +# +# anything with fw tag of 1 is passed on with a tcindex value 1 +#if it doesnt exceed its allocated rate (CIR/CBS) +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 handle 1 fw \ +$meter1 \ +continue flowid 4:1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 handle 1 fw \ +$meter1a \ +continue flowid 4:1 +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 handle 1 fw \ +$meter2 \ +continue flowid 4:2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 handle 1 fw \ +$meter2a \ +continue flowid 4:2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 handle 1 fw \ +$meter3 \ +continue flowid 4:3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 handle 1 fw \ +$meter3a \ +drop flowid 4:3 +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 handle 2 fw \ +$meter5 \ +drop flowid 4:4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:4 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping (using tcindex; could easily have +# replaced it with the fw classifier instead) +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/Edge32-cb-u32 b/examples/diffserv/Edge32-cb-u32 new file mode 100644 index 0000000..cc2ebb4 --- /dev/null +++ b/examples/diffserv/Edge32-cb-u32 @@ -0,0 +1,145 @@ +#! /bin/sh +# +# sample script on using the ingress capabilities using u32 classifier +# This script tags tcindex based on metering on the ingress +# interface the result is used for fast classification and re-marking +# on the egress interface +# This is an example of a color blind mode marker with PIR configured +# based on draft-wahjak-mcm-00.txt (section 3.2) +# +#path to various utilities; +#change to reflect yours. +# +IPROUTE=/root/DS-6-beta/iproute2-990530-dsing +TC=$IPROUTE/tc/tc +IP=$IPROUTE/ip/ip +INDEV=eth2 +EGDEV="dev eth1" +CIR1=1000kbit +CIR2=1000kbit +# The PIR is the excess (in addition to the CIR i.e if always +# going to the PIR --> average rate is CIR+PIR) +PIR1=1000kbit +PIR2=500kbit + +#The CBS is about 60 MTU sized packets +CBS1=90k +CBS2=90k +#the EBS is about 10 max sized packets +EBS1=15k +EBS2=15k +# The meters +meter1=" police rate $CIR1 burst $CBS1 " +meter1a=" police rate $PIR1 burst $EBS1 " +meter2=" police rate $CIR2 burst $CBS1 " +meter2a="police rate $PIR2 burst $CBS1 " +meter3=" police rate $CIR2 burst $CBS2 " +meter3a=" police rate $PIR2 burst $EBS2 " +meter4=" police rate $CIR1 burst $CBS2 " +meter5=" police rate $CIR1 burst $CBS2 " + + +# install the ingress qdisc on the ingress interface +############################################################ +$TC qdisc add dev $INDEV handle ffff: ingress +############################################################ +# +############################################################ + +# All packets are marked with a tcindex value which is used on the egress +# NOTE: tcindex 1 maps to AF41, 2->AF42, 3->AF43, 4->BE +# +#anything from subnet 10.2.0.2/24 is passed on with a tcindex value 1 +#if it doesnt exceed its CIR/CBS + PIR/EBS +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 1 u32 \ +match ip src 10.2.0.0/24 $meter1 \ +continue flowid :1 +$TC filter add dev $INDEV parent ffff: protocol ip prio 2 u32 \ +match ip src 10.2.0.0/24 $meter1a \ +continue flowid :1 + +# +# if it exceeds the above but not the extra rate/burst below, it gets a +#tcindex value of 2 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 3 u32 \ +match ip src 10.2.0.0/24 $meter2 \ +continue flowid :2 +$TC filter add dev $INDEV parent ffff: protocol ip prio 4 u32 \ +match ip src 10.2.0.0/24 $meter2a \ +continue flowid :2 +# +# if it exceeds the above but not the rule below, it gets a tcindex value +# of 3 +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 5 u32 \ +match ip src 10.2.0.0/24 $meter3 \ +continue flowid :3 +$TC filter add dev $INDEV parent ffff: protocol ip prio 6 u32 \ +match ip src 10.2.0.0/24 $meter3a \ +drop flowid :3 +# +# +# Anything else (not from the subnet 10.2.0.24/24) gets discarded if it +# exceeds 1Mbps and by default goes to BE if it doesnt +# +$TC filter add dev $INDEV parent ffff: protocol ip prio 7 u32 \ +match ip src 0/0 $meter5 \ +drop flowid :4 + + +######################## Egress side ######################## + + +# attach a dsmarker +# +$TC qdisc add $EGDEV handle 1:0 root dsmark indices 64 +# +# values of the DSCP to change depending on the class +#note that the ECN bits are masked out +# +#AF41 (0x88 is 0x22 shifted to the right by two bits) +# +$TC class change $EGDEV classid 1:1 dsmark mask 0x3 \ + value 0x88 +#AF42 +$TC class change $EGDEV classid 1:2 dsmark mask 0x3 \ + value 0x90 +#AF43 +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x98 +#BE +$TC class change $EGDEV classid 1:3 dsmark mask 0x3 \ + value 0x0 +# +# +# The class mapping +# +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 1 tcindex classid 1:1 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 2 tcindex classid 1:2 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 3 tcindex classid 1:3 +$TC filter add $EGDEV parent 1:0 protocol ip prio 1 \ + handle 4 tcindex classid 1:4 +# + +# +echo "---- qdisc parameters Ingress ----------" +$TC qdisc ls dev $INDEV +echo "---- Class parameters Ingress ----------" +$TC class ls dev $INDEV +echo "---- filter parameters Ingress ----------" +$TC filter ls dev $INDEV parent ffff: + +echo "---- qdisc parameters Egress ----------" +$TC qdisc ls $EGDEV +echo "---- Class parameters Egress ----------" +$TC class ls $EGDEV +echo "---- filter parameters Egress ----------" +$TC filter ls $EGDEV parent 1:0 +# +#deleting the ingress qdisc +#$TC qdisc del $INDEV ingress diff --git a/examples/diffserv/README b/examples/diffserv/README new file mode 100644 index 0000000..ec91d63 --- /dev/null +++ b/examples/diffserv/README @@ -0,0 +1,98 @@ + +Note all these are mere examples which can be customized to your needs + +AFCBQ +----- +AF PHB built using CBQ, DSMARK,GRED (default in GRIO mode) ,RED for BE +and the tcindex classifier with some algorithmic mapping + +EFCBQ +----- +EF PHB built using CBQ (for rate control and prioritization), +DSMARK( to remark DSCPs), tcindex classifier and RED for the BE +traffic. + +EFPRIO +------ +EF PHB using the PRIO scheduler, Token Bucket to rate control EF, +tcindex classifier, DSMARK to remark, and RED for the BE traffic + +EDGE scripts +============== + +CB-3(1|2)-(u32/chains) +====================== + + +The major differences are that the classifier is u32 on -u32 extension +and IPchains on the chains extension. CB stands for color Blind +and 31 is for the mode where only a CIR and CBS are defined whereas +32 stands for a mode where a CIR/CBS + PIR/EBS are defined. + +Color Blind (CB) +==========-----= +We look at one special subnet that we are interested in for simplicty +reasons to demonstrate the capability. We send the packets from that +subnet to AF4*, BE or end up dropping depending on the metering results. + + +The algorithm overview is as follows: + +*classify: + +**case: subnet X +---------------- + if !exceed meter1 tag as AF41 + else + if !exceed meter2 tag as AF42 + else + if !exceed meter 3 tag as AF43 + else + drop + +default case: Any other subnet +------------------------------- + if !exceed meter 5 tag as AF43 + else + drop + + +One Egress side change the DSCPs of the packets to reflect AF4* and BE +based on the tags from the ingress. + +------------------------------------------------------------- + +Color Aware +=========== + +Define some meters with + policing and give them IDs eg + +meter1=police index 1 rate $CIR1 burst $CBS1 +meter2=police index 2 rate $CIR2 burst $CBS2 etc + +General overview: +classify based on the DSCPs and use the policer ids to decide tagging + + +*classify on ingress: + +switch (dscp) { + case AF41: /* tos&0xfc == 0x88 */ + if (!exceed meter1) break; + case AF42: /* tos&0xfc == 0x90 */ + if (!exceed meter2) { + tag as AF42; + break; + } + case AF43: /* tos&0xfc == 0x98 */ + if (!exceed meter3) { + tag as AF43; + break; + } else + drop; + default: + if (!exceed meter4) tag as BE; + else drop; +} + +On the Egress side mark the proper AF tags diff --git a/examples/diffserv/afcbq b/examples/diffserv/afcbq new file mode 100644 index 0000000..10d6d93 --- /dev/null +++ b/examples/diffserv/afcbq @@ -0,0 +1,105 @@ +#!/usr/bin/perl +# +# +# AF using CBQ for a single interface eth0 +# 4 AF classes using GRED and one BE using RED +# Things you might want to change: +# - the device bandwidth (set at 10Mbits) +# - the bandwidth allocated for each AF class and the BE class +# - the drop probability associated with each AF virtual queue +# +# AF DSCP values used (based on AF draft 04) +# ----------------------------------------- +# AF DSCP values +# AF1 1. 0x0a 2. 0x0c 3. 0x0e +# AF2 1. 0x12 2. 0x14 3. 0x16 +# AF3 1. 0x1a 2. 0x1c 3. 0x1e +# AF4 1. 0x22 2. 0x24 3. 0x26 + +# +# +# A simple DSCP-class relationship formula used to generate +# values in the for loop of this script; $drop stands for the +# DP +# $dscp = ($class*8+$drop*2) +# +# if you use GRIO buffer sharing, then GRED priority is set as follows: +# $gprio=$drop+1; +# + +$TC = "/usr/src/iproute2-current/tc/tc"; +$DEV = "dev lo"; +$DEV = "dev eth1"; +$DEV = "dev eth0"; +# the BE-class number +$beclass = "5"; + +#GRIO buffer sharing on or off? +$GRIO = ""; +$GRIO = "grio"; +# The bandwidth of your device +$linerate="10Mbit"; +# The BE and AF rates +%rate_table=(); +$berate="1500Kbit"; +$rate_table{"AF1rate"}="1500Kbit"; +$rate_table{"AF2rate"}="1500Kbit"; +$rate_table{"AF3rate"}="1500Kbit"; +$rate_table{"AF4rate"}="1500Kbit"; +# +# +# +print "\n# --- General setup ---\n"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex mask 0xfc " . + "shift 2 pass_on\n"; + #"shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth $linerate ". + "cell 8 avpkt 1000 mpu 64\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 tcindex ". + "mask 0xf0 shift 4 pass_on\n"; +for $class (1..4) { + print "\n# --- AF Class $class specific setup---\n"; + $AFrate=sprintf("AF%drate",$class); + print "$TC class add $DEV parent 2:0 classid 2:$class cbq ". + "bandwidth $linerate rate $rate_table{$AFrate} avpkt 1000 prio ". + (6-$class)." bounded allot 1514 weight 1 maxburst 21\n"; + print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle $class ". + "tcindex classid 2:$class\n"; + print "$TC qdisc add $DEV parent 2:$class gred setup DPs 3 default 2 ". + "$GRIO\n"; +# +# per DP setup +# + for $drop (1..3) { + print "\n# --- AF Class $class DP $drop---\n"; + $dscp = $class*8+$drop*2; + $tcindex = sprintf("1%x%x",$class,$drop); + print "$TC filter add $DEV parent 1:0 protocol ip prio 1 ". + "handle $dscp tcindex classid 1:$tcindex\n"; + $prob = $drop*0.02; + if ($GRIO) { + $gprio = $drop+1; + print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ". + "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ". + "probability $prob ". + "prio $gprio\n"; + } else { + print "$TC qdisc change $DEV parent 2:$class gred limit 60KB min 15KB ". + "max 45KB burst 20 avpkt 1000 bandwidth $linerate DP $drop ". + "probability $prob \n"; + } + } +} +# +# +print "\n#------BE Queue setup------\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 1:1\n"; +print "$TC class add $DEV parent 2:0 classid 2:$beclass cbq ". + "bandwidth $linerate rate $berate avpkt 1000 prio 6 " . + "bounded allot 1514 weight 1 maxburst 21 \n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 handle 0 tcindex ". + "classid 2:5\n"; +print "$TC qdisc add $DEV parent 2:5 red limit 60KB min 15KB max 45KB ". + "burst 20 avpkt 1000 bandwidth $linerate probability 0.4\n"; diff --git a/examples/diffserv/ef-prio b/examples/diffserv/ef-prio new file mode 100644 index 0000000..48611bd --- /dev/null +++ b/examples/diffserv/ef-prio @@ -0,0 +1,25 @@ +#!/usr/bin/perl +$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc"; +$DEV = "dev eth1"; +$efrate="1.5Mbit"; +$MTU="1.5kB"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ". + "mask 0xfc shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 prio\n"; +# +# EF class: Maximum about one MTU sized packet allowed on the queue +# +print "$TC qdisc add $DEV parent 2:1 tbf rate $efrate burst $MTU limit 1.6kB\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ". + "handle 0x2e tcindex classid 2:1 pass_on\n"; +# +# BE class +# +print "#BE class(2:2) \n"; +print "$TC qdisc add $DEV parent 2:2 red limit 60KB ". + "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ". + "probability 0.4\n"; +# +print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 2:2 pass_on\n"; diff --git a/examples/diffserv/efcbq b/examples/diffserv/efcbq new file mode 100644 index 0000000..bcc437b --- /dev/null +++ b/examples/diffserv/efcbq @@ -0,0 +1,31 @@ +#!/usr/bin/perl +# +$TC = "/root/DS-6-beta/iproute2-990530-dsing/tc/tc"; +$DEV = "dev eth1"; +print "$TC qdisc add $DEV handle 1:0 root dsmark indices 64 set_tc_index\n"; +print "$TC filter add $DEV parent 1:0 protocol ip prio 1 tcindex ". + "mask 0xfc shift 2\n"; +print "$TC qdisc add $DEV parent 1:0 handle 2:0 cbq bandwidth ". + "10Mbit cell 8 avpkt 1000 mpu 64\n"; +# +# EF class +# +print "$TC class add $DEV parent 2:0 classid 2:1 cbq bandwidth ". + "10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated ". + "allot 1514 weight 1 maxburst 10 \n"; +# packet fifo for EF? +print "$TC qdisc add $DEV parent 2:1 pfifo limit 5\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 1 ". + "handle 0x2e tcindex classid 2:1 pass_on\n"; +# +# BE class +# +print "#BE class(2:2) \n"; +print "$TC class add $DEV parent 2:0 classid 2:2 cbq bandwidth ". + "10Mbit rate 5Mbit avpkt 1000 prio 7 allot 1514 weight 1 ". + "maxburst 21 borrow split 2:0 defmap 0xffff \n"; +print "$TC qdisc add $DEV parent 2:2 red limit 60KB ". + "min 15KB max 45KB burst 20 avpkt 1000 bandwidth 10Mbit ". + "probability 0.4\n"; +print "$TC filter add $DEV parent 2:0 protocol ip prio 2 ". + "handle 0 tcindex mask 0 classid 2:2 pass_on\n"; diff --git a/examples/diffserv/regression-testing b/examples/diffserv/regression-testing new file mode 100644 index 0000000..0ec705c --- /dev/null +++ b/examples/diffserv/regression-testing @@ -0,0 +1,125 @@ + +These were the tests done to validate the Diffserv scripts. +This document will be updated continously. If you do more +thorough validation testing please post the details to the +diffserv mailing list. +Nevertheless, these tests should serve for basic validation. + +AFCBQ, EFCBQ, EFPRIO +---------------------- + +generate all possible DSCPs and observe that they +get sent to the proper classes. In the case of AF also +to the correct Virtual Queues. + +Edge1 +----- +generate TOS values 0x0,0x10,0xbb each with IP addresses +10.2.0.24 (mark 1), 10.2.0.3 (mark2) and 10.2.0.30 (mark 3) +and observe that they get marked as expected. + +Edge2 +----- + +-Repeat the tests in Edge1 +-ftp with data direction from 10.2.0.2 + *observe that the metering/policing works correctly (and the marking + as well). In this case the mark used will be 3 + +Edge31-cb-chains +---------------- + +-ftp with data direction from 10.2.0.2 + + *observe that the metering/policing works correctly (and the marking + as well). In this case the mark used will be 1. + + Metering: The data throughput should not exceed 2*CIR1 + 2*CIR2 + which is roughly: 5mbps + + Marking: the should be a variation of marked packets: + AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0) + +More tests required to see the interaction of several sources (other +than subnet 10.2.0.0/24). + +Edge31-ca-u32 +-------------- + +Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the +discard port of 10.1.0.2 (behind eth1) + +1) generate with src tos = 0x88 + Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2 + approximately 5mbps + Marking: Should vary between 0x88,0x90,0x98 and 0x0 + +2) generate with src tos = 0x90 + Metering: Allocated throughput should not exceed CIR1 + 2*CIR2 + approximately 3.5mbps + Marking: Should vary between 0x90,0x98 and 0x0 + +3) generate with src tos = 0x98 + Metering: Allocated throughput should not exceed CIR1 + CIR2 + approximately 2.5mbps + Marking: Should vary between 0x98 and 0x0 + +4) generate with src tos any other than the above + Metering: Allocated throughput should not exceed CIR1 + approximately 1.5mbps + Marking: Should be consistent at 0x0 + +TODO: Testing on how each color shares when all 4 types of packets +are going through the edge device + +Edge32-cb-u32, Edge32-cb-chains +------------------------------- + +-ftp with data direction from 10.2.0.2 + + *observe that the metering/policing works correctly (and the marking + as well). + + Metering: + The data throughput should not exceed 2*CIR1 + 2*CIR2 + + 2*PIR2 + PIR1 for u32 which is roughly: 6mbps + The data throughput should not exceed 2*CIR1 + 5*CIR2 + for chains which is roughly: 6mbps + + Marking: the should be a variation of marked packets: + AF41(TOS=0x88) AF42(0x90) AF43(0x98) and BE (0x0) + +TODO: +-More tests required to see the interaction of several sources (other +than subnet 10.2.0.0/24). +-More tests needed to capture stats on how many times the CIR was exceeded +but the data was not remarked etc. + +Edge32-ca-u32 +-------------- + +Generate data using modified tcpblast from 10.2.0.2 (behind eth2) to the +discard port of 10.1.0.2 (behind eth1) + +1) generate with src tos = 0x88 + Metering: Allocated throughput should not exceed 2*CIR1 + 2*CIR2 + +PIR1 -- approximately 4mbps + Marking: Should vary between 0x88,0x90,0x98 and 0x0 + +2) generate with src tos = 0x90 + Metering: Allocated throughput should not exceed CIR1 + 2*CIR2 + + 2* PIR2 approximately 3mbps + Marking: Should vary between 0x90,0x98 and 0x0 + +3) generate with src tos = 0x98 + Metering: Allocated throughput should not exceed PIR1+ CIR1 + CIR2 + approximately 2.5mbps + Marking: Should vary between 0x98 and 0x0 + +4) generate with src tos any other than the above + Metering: Allocated throughput should not exceed CIR1 + approximately 1mbps + Marking: Should be consistent at 0x0 + +TODO: Testing on how each color shares when all 4 types of packets +are going through the edge device diff --git a/include/SNAPSHOT.h b/include/SNAPSHOT.h new file mode 100644 index 0000000..8375d76 --- /dev/null +++ b/include/SNAPSHOT.h @@ -0,0 +1 @@ +static char SNAPSHOT[] = "050314"; diff --git a/include/ip6tables.h b/include/ip6tables.h new file mode 100644 index 0000000..8360617 --- /dev/null +++ b/include/ip6tables.h @@ -0,0 +1,141 @@ +#ifndef _IP6TABLES_USER_H +#define _IP6TABLES_USER_H + +#include "iptables_common.h" +#include "libiptc/libip6tc.h" + +struct ip6tables_rule_match +{ + struct ip6tables_rule_match *next; + + struct ip6tables_match *match; +}; + +/* Include file for additions: new matches and targets. */ +struct ip6tables_match +{ + struct ip6tables_match *next; + + ip6t_chainlabel name; + + const char *version; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct ip6t_entry_match *m, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ip6t_entry *entry, + unsigned int *nfcache, + struct ip6t_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + void (*print)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match, int numeric); + + /* Saves the union ipt_matchinfo in parsable form to stdout. */ + void (*save)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ip6t_entry_match *m; + unsigned int mflags; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +struct ip6tables_target +{ + struct ip6tables_target *next; + + ip6t_chainlabel name; + + const char *version; + + /* Size of target data. */ + size_t size; + + /* Size of target data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct ip6t_entry_target *t, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ip6t_entry *entry, + struct ip6t_entry_target **target); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const struct ip6t_ip6 *ip, + const struct ip6t_entry_target *target); + + /* Pointer to list of extra command-line options */ + struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ip6t_entry_target *t; + unsigned int tflags; + unsigned int used; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +extern int line; + +/* Your shared library should call one of these. */ +extern void register_match6(struct ip6tables_match *me); +extern void register_target6(struct ip6tables_target *me); + +extern int do_command6(int argc, char *argv[], char **table, + ip6tc_handle_t *handle); +/* Keeping track of external matches and targets: linked lists. */ +extern struct ip6tables_match *ip6tables_matches; +extern struct ip6tables_target *ip6tables_targets; + +enum ip6t_tryload { + DONT_LOAD, + TRY_LOAD, + LOAD_MUST_SUCCEED +}; + +extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload); +extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload, struct ip6tables_rule_match **match); + +extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle); +extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); +extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle); +extern int ip6tables_insmod(const char *modname, const char *modprobe); + +#endif /*_IP6TABLES_USER_H*/ diff --git a/include/iptables.h b/include/iptables.h new file mode 100644 index 0000000..5aca69a --- /dev/null +++ b/include/iptables.h @@ -0,0 +1,155 @@ +#ifndef _IPTABLES_USER_H +#define _IPTABLES_USER_H + +#include "iptables_common.h" +#include "libiptc/libiptc.h" + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +struct iptables_rule_match +{ + struct iptables_rule_match *next; + + struct iptables_match *match; +}; + +/* Include file for additions: new matches and targets. */ +struct iptables_match +{ + struct iptables_match *next; + + ipt_chainlabel name; + + const char *version; + + /* Size of match data. */ + size_t size; + + /* Size of match data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the match. */ + void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + unsigned int *nfcache, + struct ipt_entry_match **match); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the match iff non-NULL: put space at end */ + void (*print)(const struct ipt_ip *ip, + const struct ipt_entry_match *match, int numeric); + + /* Saves the match info in parsable form to stdout. */ + void (*save)(const struct ipt_ip *ip, + const struct ipt_entry_match *match); + + /* Pointer to list of extra command-line options */ + const struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ipt_entry_match *m; + unsigned int mflags; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +struct iptables_target +{ + struct iptables_target *next; + + ipt_chainlabel name; + + const char *version; + + /* Size of target data. */ + size_t size; + + /* Size of target data relevent for userspace comparison purposes */ + size_t userspacesize; + + /* Function which prints out usage message. */ + void (*help)(void); + + /* Initialize the target. */ + void (*init)(struct ipt_entry_target *t, unsigned int *nfcache); + + /* Function which parses command options; returns true if it + ate an option */ + int (*parse)(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + struct ipt_entry_target **target); + + /* Final check; exit if not ok. */ + void (*final_check)(unsigned int flags); + + /* Prints out the target iff non-NULL: put space at end */ + void (*print)(const struct ipt_ip *ip, + const struct ipt_entry_target *target, int numeric); + + /* Saves the targinfo in parsable form to stdout. */ + void (*save)(const struct ipt_ip *ip, + const struct ipt_entry_target *target); + + /* Pointer to list of extra command-line options */ + struct option *extra_opts; + + /* Ignore these men behind the curtain: */ + unsigned int option_offset; + struct ipt_entry_target *t; + unsigned int tflags; + unsigned int used; +#ifdef NO_SHARED_LIBS + unsigned int loaded; /* simulate loading so options are merged properly */ +#endif +}; + +extern int line; + +/* Your shared library should call one of these. */ +extern void register_match(struct iptables_match *me); +extern void register_target(struct iptables_target *me); + +extern struct in_addr *dotted_to_addr(const char *dotted); +extern char *addr_to_dotted(const struct in_addr *addrp); +extern char *addr_to_anyname(const struct in_addr *addr); +extern char *mask_to_dotted(const struct in_addr *mask); + +extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs); +extern u_int16_t parse_protocol(const char *s); + +extern int do_command(int argc, char *argv[], char **table, + iptc_handle_t *handle); +/* Keeping track of external matches and targets: linked lists. */ +extern struct iptables_match *iptables_matches; +extern struct iptables_target *iptables_targets; + +enum ipt_tryload { + DONT_LOAD, + TRY_LOAD, + LOAD_MUST_SUCCEED +}; + +extern struct iptables_target *find_target(const char *name, enum ipt_tryload); +extern struct iptables_match *find_match(const char *name, enum ipt_tryload, struct iptables_rule_match **match); + +extern int delete_chain(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle); +extern int flush_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle); +extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), + int verbose, int builtinstoo, iptc_handle_t *handle); +#endif /*_IPTABLES_USER_H*/ diff --git a/include/iptables_common.h b/include/iptables_common.h new file mode 100644 index 0000000..e3b99aa --- /dev/null +++ b/include/iptables_common.h @@ -0,0 +1,37 @@ +#ifndef _IPTABLES_COMMON_H +#define _IPTABLES_COMMON_H +/* Shared definitions between ipv4 and ipv6. */ + +enum exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM +}; +extern void exit_printhelp(void) __attribute__((noreturn)); +extern void exit_tryhelp(int) __attribute__((noreturn)); +int check_inverse(const char option[], int *invert, int *optind, int argc); +extern int string_to_number(const char *, + unsigned int, + unsigned int, + unsigned int *); +extern int string_to_number_l(const char *, + unsigned long int, + unsigned long int, + unsigned long *); +extern int string_to_number_ll(const char *, + unsigned long long int, + unsigned long long int, + unsigned long long *); +extern int iptables_insmod(const char *modname, const char *modprobe); +void exit_error(enum exittype, char *, ...)__attribute__((noreturn, + format(printf,2,3))); +extern const char *program_name, *program_version; + +#ifdef NO_SHARED_LIBS +# ifdef _INIT +# define _init _INIT +# endif + extern void init_extensions(void); +#endif + +#endif /*_IPTABLES_COMMON_H*/ diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h new file mode 100644 index 0000000..18861fe --- /dev/null +++ b/include/libiptc/ipt_kernel_headers.h @@ -0,0 +1,27 @@ +/* This is the userspace/kernel interface for Generic IP Chains, + required for libc6. */ +#ifndef _FWCHAINS_KERNEL_HEADERS_H +#define _FWCHAINS_KERNEL_HEADERS_H + +#include <limits.h> + +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <netinet/ip.h> +#include <netinet/in.h> +#include <netinet/ip_icmp.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <net/if.h> +#include <sys/types.h> +#else /* libc5 */ +#include <sys/socket.h> +#include <linux/ip.h> +#include <linux/in.h> +#include <linux/if.h> +#include <linux/icmp.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/types.h> +#include <linux/in6.h> +#endif +#endif diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h new file mode 100644 index 0000000..7a247c4 --- /dev/null +++ b/include/libiptc/libip6tc.h @@ -0,0 +1,154 @@ +#ifndef _LIBIP6TC_H +#define _LIBIP6TC_H +/* Library which manipulates firewall rules. Version 0.2. */ + +#include <libiptc/ipt_kernel_headers.h> +#include <linux/netfilter_ipv6/ip6_tables.h> + +#ifndef IP6T_MIN_ALIGN +#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry)) +#endif +#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1)) + +typedef char ip6t_chainlabel[32]; + +#define IP6TC_LABEL_ACCEPT "ACCEPT" +#define IP6TC_LABEL_DROP "DROP" +#define IP6TC_LABEL_QUEUE "QUEUE" +#define IP6TC_LABEL_RETURN "RETURN" + +/* Transparent handle type. */ +typedef struct ip6tc_handle *ip6tc_handle_t; + +/* Does this chain exist? */ +int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +ip6tc_handle_t ip6tc_init(const char *tablename); + +/* Cleanup after ip6tc_init(). */ +void ip6tc_free(ip6tc_handle_t *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *ip6tc_first_chain(ip6tc_handle_t *handle); +const char *ip6tc_next_chain(ip6tc_handle_t *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ip6t_entry *ip6tc_first_rule(const char *chain, + ip6tc_handle_t *handle); + +/* Returns NULL when rules run out. */ +const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev, + ip6tc_handle_t *handle); + +/* Returns a pointer to the target name of this position. */ +const char *ip6tc_get_target(const struct ip6t_entry *e, + ip6tc_handle_t *handle); + +/* Is this a built-in chain? */ +int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle); + +/* Get the policy of a given built-in chain */ +const char *ip6tc_get_policy(const char *chain, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `fw' in chain `chain' into position `rulenum'. */ +int ip6tc_insert_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Atomically replace rule `rulenum' in `chain' with `fw'. */ +int ip6tc_replace_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Append entry `fw' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int ip6tc_append_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *e, + ip6tc_handle_t *handle); + +/* Delete the first rule in `chain' which matches `fw'. */ +int ip6tc_delete_entry(const ip6t_chainlabel chain, + const struct ip6t_entry *origfw, + unsigned char *matchmask, + ip6tc_handle_t *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int ip6tc_delete_num_entry(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* Check the packet `fw' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *ip6tc_check_packet(const ip6t_chainlabel chain, + struct ip6t_entry *, + ip6tc_handle_t *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int ip6tc_flush_entries(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Zeroes the counters in a chain. */ +int ip6tc_zero_entries(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Creates a new chain. */ +int ip6tc_create_chain(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Deletes a chain. */ +int ip6tc_delete_chain(const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* Renames a chain. */ +int ip6tc_rename_chain(const ip6t_chainlabel oldname, + const ip6t_chainlabel newname, + ip6tc_handle_t *handle); + +/* Sets the policy on a built-in chain. */ +int ip6tc_set_policy(const ip6t_chainlabel chain, + const ip6t_chainlabel policy, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* Get the number of references to this chain */ +int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain, + ip6tc_handle_t *handle); + +/* read packet and byte counters for a specific rule */ +struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* zero packet and byte counters for a specific rule */ +int ip6tc_zero_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + ip6tc_handle_t *handle); + +/* set packet and byte counters for a specific rule */ +int ip6tc_set_counter(const ip6t_chainlabel chain, + unsigned int rulenum, + struct ip6t_counters *counters, + ip6tc_handle_t *handle); + +/* Makes the actual changes. */ +int ip6tc_commit(ip6tc_handle_t *handle); + +/* Get raw socket. */ +int ip6tc_get_raw_socket(); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *ip6tc_strerror(int err); + +/* Return prefix length, or -1 if not contiguous */ +int ipv6_prefix_length(const struct in6_addr *a); + +#endif /* _LIBIP6TC_H */ diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h new file mode 100644 index 0000000..7628bda --- /dev/null +++ b/include/libiptc/libiptc.h @@ -0,0 +1,166 @@ +#ifndef _LIBIPTC_H +#define _LIBIPTC_H +/* Library which manipulates filtering rules. */ + +#include <libiptc/ipt_kernel_headers.h> +#include <linux/netfilter_ipv4/ip_tables.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef IPT_MIN_ALIGN +/* ipt_entry has pointers and u_int64_t's in it, so if you align to + it, you'll also align to any crazy matches and targets someone + might write */ +#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry)) +#endif + +#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1)) + +typedef char ipt_chainlabel[32]; + +#define IPTC_LABEL_ACCEPT "ACCEPT" +#define IPTC_LABEL_DROP "DROP" +#define IPTC_LABEL_QUEUE "QUEUE" +#define IPTC_LABEL_RETURN "RETURN" + +/* Transparent handle type. */ +typedef struct iptc_handle *iptc_handle_t; + +/* Does this chain exist? */ +int iptc_is_chain(const char *chain, const iptc_handle_t handle); + +/* Take a snapshot of the rules. Returns NULL on error. */ +iptc_handle_t iptc_init(const char *tablename); + +/* Cleanup after iptc_init(). */ +void iptc_free(iptc_handle_t *h); + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char *iptc_first_chain(iptc_handle_t *handle); +const char *iptc_next_chain(iptc_handle_t *handle); + +/* Get first rule in the given chain: NULL for empty chain. */ +const struct ipt_entry *iptc_first_rule(const char *chain, + iptc_handle_t *handle); + +/* Returns NULL when rules run out. */ +const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, + iptc_handle_t *handle); + +/* Returns a pointer to the target name of this entry. */ +const char *iptc_get_target(const struct ipt_entry *e, + iptc_handle_t *handle); + +/* Is this a built-in chain? */ +int iptc_builtin(const char *chain, const iptc_handle_t handle); + +/* Get the policy of a given built-in chain */ +const char *iptc_get_policy(const char *chain, + struct ipt_counters *counter, + iptc_handle_t *handle); + +/* These functions return TRUE for OK or 0 and set errno. If errno == + 0, it means there was a version error (ie. upgrade libiptc). */ +/* Rule numbers start at 1 for the first rule. */ + +/* Insert the entry `e' in chain `chain' into position `rulenum'. */ +int iptc_insert_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Atomically replace rule `rulenum' in `chain' with `e'. */ +int iptc_replace_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Append entry `e' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int iptc_append_entry(const ipt_chainlabel chain, + const struct ipt_entry *e, + iptc_handle_t *handle); + +/* Delete the first rule in `chain' which matches `e', subject to + matchmask (array of length == origfw) */ +int iptc_delete_entry(const ipt_chainlabel chain, + const struct ipt_entry *origfw, + unsigned char *matchmask, + iptc_handle_t *handle); + +/* Delete the rule in position `rulenum' in `chain'. */ +int iptc_delete_num_entry(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* Check the packet `e' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char *iptc_check_packet(const ipt_chainlabel chain, + struct ipt_entry *entry, + iptc_handle_t *handle); + +/* Flushes the entries in the given chain (ie. empties chain). */ +int iptc_flush_entries(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Zeroes the counters in a chain. */ +int iptc_zero_entries(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Creates a new chain. */ +int iptc_create_chain(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Deletes a chain. */ +int iptc_delete_chain(const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* Renames a chain. */ +int iptc_rename_chain(const ipt_chainlabel oldname, + const ipt_chainlabel newname, + iptc_handle_t *handle); + +/* Sets the policy on a built-in chain. */ +int iptc_set_policy(const ipt_chainlabel chain, + const ipt_chainlabel policy, + struct ipt_counters *counters, + iptc_handle_t *handle); + +/* Get the number of references to this chain */ +int iptc_get_references(unsigned int *ref, + const ipt_chainlabel chain, + iptc_handle_t *handle); + +/* read packet and byte counters for a specific rule */ +struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* zero packet and byte counters for a specific rule */ +int iptc_zero_counter(const ipt_chainlabel chain, + unsigned int rulenum, + iptc_handle_t *handle); + +/* set packet and byte counters for a specific rule */ +int iptc_set_counter(const ipt_chainlabel chain, + unsigned int rulenum, + struct ipt_counters *counters, + iptc_handle_t *handle); + +/* Makes the actual changes. */ +int iptc_commit(iptc_handle_t *handle); + +/* Get raw socket. */ +int iptc_get_raw_socket(void); + +/* Translates errno numbers into more human-readable form than strerror. */ +const char *iptc_strerror(int err); + +#ifdef __cplusplus +} +#endif + + +#endif /* _LIBIPTC_H */ diff --git a/include/libnetlink.h b/include/libnetlink.h new file mode 100644 index 0000000..63cc3c8 --- /dev/null +++ b/include/libnetlink.h @@ -0,0 +1,57 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); + +typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, + struct nlmsghdr *n, void *); +extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2); +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg); +extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int); + + +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen); +extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len); + +#define parse_rtattr_nested(tb, max, rta) \ + (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) + +extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, + void *jarg); +extern int rtnl_from_file(FILE *, rtnl_filter_t handler, + void *jarg); + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +#endif /* __LIBNETLINK_H__ */ + diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h new file mode 100644 index 0000000..13f4e74 --- /dev/null +++ b/include/linux/gen_stats.h @@ -0,0 +1,67 @@ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +#include <linux/types.h> + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * struct gnet_stats_basic - byte/packet throughput statistics + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic +{ + __u64 bytes; + __u32 packets; +}; + +/** + * struct gnet_stats_rate_est - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est +{ + __u32 bps; + __u32 pps; +}; + +/** + * struct gnet_stats_queue - queuing statistics + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + * @overlimits: number of enqueues over the limit + */ +struct gnet_stats_queue +{ + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * struct gnet_estimator - rate estimator configuration + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h new file mode 100644 index 0000000..7346ead --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -0,0 +1,347 @@ +/* + * 25-Jul-1998 Major changes to allow for ip chain table + * + * 3-Jan-2000 Named tables to allow packet selection for different uses. + */ + +/* + * Format of an IP firewall descriptor + * + * src, dst, src_mask, dst_mask are always stored in network byte order. + * flags are stored in host byte order (of course). + * Port numbers are stored in HOST byte order. + */ + +#ifndef _IPTABLES_H +#define _IPTABLES_H + +#include <linux/compiler.h> +#include <linux/netfilter_ipv4.h> + +#define IPT_FUNCTION_MAXNAMELEN 30 +#define IPT_TABLE_MAXNAMELEN 32 + +/* Yes, Virginia, you have to zero the padding. */ +struct ipt_ip { + /* Source and destination IP addr */ + struct in_addr src, dst; + /* Mask for src and dest IP addr */ + struct in_addr smsk, dmsk; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + + /* Protocol, 0 = ANY */ + u_int16_t proto; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; + +struct ipt_entry_match +{ + union { + struct { + u_int16_t match_size; + + /* Used by userspace */ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; + } user; + struct { + u_int16_t match_size; + + /* Used inside the kernel */ + struct ipt_match *match; + } kernel; + + /* Total length */ + u_int16_t match_size; + } u; + + unsigned char data[0]; +}; + +struct ipt_entry_target +{ + union { + struct { + u_int16_t target_size; + + /* Used by userspace */ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; + } user; + struct { + u_int16_t target_size; + + /* Used inside the kernel */ + struct ipt_target *target; + } kernel; + + /* Total length */ + u_int16_t target_size; + } u; + + unsigned char data[0]; +}; + +struct ipt_standard_target +{ + struct ipt_entry_target target; + int verdict; +}; + +struct ipt_counters +{ + u_int64_t pcnt, bcnt; /* Packet and byte counters */ +}; + +/* Values for "flag" field in struct ipt_ip (general ip structure). */ +#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ +#define IPT_F_MASK 0x01 /* All possible flag bits mask. */ + +/* Values for "inv" field in struct ipt_ip. */ +#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +#define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ +#define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */ +#define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ +#define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ +#define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */ +#define IPT_INV_PROTO 0x40 /* Invert the sense of PROTO. */ +#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */ + +/* This structure defines each of the firewall rules. Consists of 3 + parts which are 1) general IP header stuff 2) match specific + stuff 3) the target to perform if the rule matches */ +struct ipt_entry +{ + struct ipt_ip ip; + + /* Mark with fields that we care about. */ + unsigned int nfcache; + + /* Size of ipt_entry + matches */ + u_int16_t target_offset; + /* Size of ipt_entry + matches + target */ + u_int16_t next_offset; + + /* Back pointer */ + unsigned int comefrom; + + /* Packet and byte counters. */ + struct ipt_counters counters; + + /* The matches (if any), then the target. */ + unsigned char elems[0]; +}; + +/* + * New IP firewall options for [gs]etsockopt at the RAW IP level. + * Unlike BSD Linux inherits IP options so you don't have to use a raw + * socket for this. Instead we check rights in the calls. */ +#define IPT_BASE_CTL 64 /* base for firewall socket options */ + +#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) +#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1) +#define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS + +#define IPT_SO_GET_INFO (IPT_BASE_CTL) +#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) +#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2) +#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3) +#define IPT_SO_GET_MAX IPT_SO_GET_REVISION_TARGET + +/* CONTINUE verdict for targets */ +#define IPT_CONTINUE 0xFFFFFFFF + +/* For standard target */ +#define IPT_RETURN (-NF_MAX_VERDICT - 1) + +/* TCP matching stuff */ +struct ipt_tcp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t option; /* TCP Option iff non-zero*/ + u_int8_t flg_mask; /* TCP flags mask byte */ + u_int8_t flg_cmp; /* TCP flags compare byte */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field in struct ipt_tcp. */ +#define IPT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IPT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IPT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */ +#define IPT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */ +#define IPT_TCP_INV_MASK 0x0F /* All possible flags. */ + +/* UDP matching stuff */ +struct ipt_udp +{ + u_int16_t spts[2]; /* Source port range. */ + u_int16_t dpts[2]; /* Destination port range. */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "invflags" field in struct ipt_udp. */ +#define IPT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */ +#define IPT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */ +#define IPT_UDP_INV_MASK 0x03 /* All possible flags. */ + +/* ICMP matching stuff */ +struct ipt_icmp +{ + u_int8_t type; /* type to match */ + u_int8_t code[2]; /* range of code */ + u_int8_t invflags; /* Inverse flags */ +}; + +/* Values for "inv" field for struct ipt_icmp. */ +#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */ + +/* The argument to IPT_SO_GET_INFO */ +struct ipt_getinfo +{ + /* Which table: caller fills this in. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* Kernel fills these in. */ + /* Which hook entry points are valid: bitmask */ + unsigned int valid_hooks; + + /* Hook entry points: one per netfilter hook. */ + unsigned int hook_entry[NF_IP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP_NUMHOOKS]; + + /* Number of entries */ + unsigned int num_entries; + + /* Size of entries. */ + unsigned int size; +}; + +/* The argument to IPT_SO_SET_REPLACE. */ +struct ipt_replace +{ + /* Which table. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* Which hook entry points are valid: bitmask. You can't + change this. */ + unsigned int valid_hooks; + + /* Number of entries */ + unsigned int num_entries; + + /* Total size of new entries */ + unsigned int size; + + /* Hook entry points. */ + unsigned int hook_entry[NF_IP_NUMHOOKS]; + + /* Underflow points. */ + unsigned int underflow[NF_IP_NUMHOOKS]; + + /* Information about old entries: */ + /* Number of counters (must be equal to current number of entries). */ + unsigned int num_counters; + + /* The old entries' counters. */ + struct ipt_counters *counters; + + /* The entries (hang off end: not really an array). */ + struct ipt_entry entries[0]; +}; + +/* The argument to IPT_SO_ADD_COUNTERS. */ +struct ipt_counters_info +{ + /* Which table. */ + char name[IPT_TABLE_MAXNAMELEN]; + + unsigned int num_counters; + + /* The counters (actually `number' of these). */ + struct ipt_counters counters[0]; +}; + +/* The argument to IPT_SO_GET_ENTRIES. */ +struct ipt_get_entries +{ + /* Which table: user fills this in. */ + char name[IPT_TABLE_MAXNAMELEN]; + + /* User fills this in: total entry size. */ + unsigned int size; + + /* The entries. */ + struct ipt_entry entrytable[0]; +}; + +/* The argument to IPT_SO_GET_REVISION_*. Returns highest revision + * kernel supports, if >= revision. */ +struct ipt_get_revision +{ + char name[IPT_FUNCTION_MAXNAMELEN-1]; + + u_int8_t revision; +}; + +/* Standard return verdict, or do jump. */ +#define IPT_STANDARD_TARGET "" +/* Error verdict. */ +#define IPT_ERROR_TARGET "ERROR" + +/* Helper functions */ +static __inline__ struct ipt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (void *)e + e->target_offset; +} + +/* fn returns 0 to continue iteration */ +#define IPT_MATCH_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ipt_entry_match *__match; \ + \ + for (__i = sizeof(struct ipt_entry); \ + __i < (e)->target_offset; \ + __i += __match->u.match_size) { \ + __match = (void *)(e) + __i; \ + \ + __ret = fn(__match , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* fn returns 0 to continue iteration */ +#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ipt_entry *__entry; \ + \ + for (__i = 0; __i < (size); __i += __entry->next_offset) { \ + __entry = (void *)(entries) + __i; \ + \ + __ret = fn(__entry , ## args); \ + if (__ret != 0) \ + break; \ + } \ + __ret; \ +}) + +/* + * Main firewall chains definitions and global var's definitions. + */ +#endif /* _IPTABLES_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h new file mode 100644 index 0000000..13828e5 --- /dev/null +++ b/include/linux/netlink.h @@ -0,0 +1,101 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include <linux/socket.h> /* for sa_family_t */ +#include <linux/types.h> + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_SKIP 1 /* Reserved for ENskip */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_TCPDIAG 4 /* TCP socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ARPD 8 +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */ + +#define MAX_LINKS 32 + +struct sockaddr_nl +{ + sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* process pid */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr +{ + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process PID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +struct nlmsgerr +{ + int error; + struct nlmsghdr msg; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h new file mode 100644 index 0000000..741d15b --- /dev/null +++ b/include/linux/pkt_cls.h @@ -0,0 +1,425 @@ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include <linux/pkt_sched.h> + +/* I think i could have done better macros ; for now this is stolen from + * some arch/mips code - jhs +*/ +#define _TC_MAKE32(x) ((x)) + +#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) +#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n)) +#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n)) +#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n)) + +/* verdict bit breakdown + * +bit 0: when set -> this packet has been munged already + +bit 1: when set -> It is ok to munge this packet + +bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded +assume loop + +bit 6,7: Where this packet was last seen +0: Above the transmit example at the socket level +1: on the Ingress +2: on the Egress + +bit 8: when set --> Request not to classify on ingress. + +bits 9,10,11: redirect counter - redirect TTL. Loop avoidance + + * + * */ + +#define TC_MUNGED _TC_MAKEMASK1(0) +#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED)) +#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED) + +#define TC_OK2MUNGE _TC_MAKEMASK1(1) +#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE)) +#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE) + +#define S_TC_VERD _TC_MAKE32(2) +#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD) +#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD) +#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD) +#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD)) + +#define S_TC_FROM _TC_MAKE32(6) +#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM) +#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM) +#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM) +#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM)) +#define AT_STACK 0x0 +#define AT_INGRESS 0x1 +#define AT_EGRESS 0x2 + +#define TC_NCLS _TC_MAKEMASK1(8) +#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS)) +#define CLR_TC_NCLS(v) ( v & ~TC_NCLS) + +#define S_TC_RTTL _TC_MAKE32(9) +#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL) +#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL) +#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL) +#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL)) + +#define S_TC_AT _TC_MAKE32(12) +#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT) +#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT) +#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT) +#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT)) + +/* Action attributes */ +enum +{ + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 +#define MAX_REC_LOOP 4 +#define MAX_RED_LOOP 4 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_JUMP 0x10000000 + +/* Action type identifiers*/ +enum +{ + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police +{ + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t +{ + __u64 install; + __u64 lastuse; + __u64 expires; +}; + +struct tc_cnt +{ + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum +{ + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum +{ + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key +{ + __u32 mask; + __u32 val; + int off; + int offmask; +}; + +struct tc_u32_sel +{ + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __u16 offmask; + __u16 off; + short offoff; + + short hoff; + __u32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark +{ + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt +{ + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum +{ + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi +{ + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo +{ + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; +}; + +/* ROUTE filter */ + +enum +{ + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum +{ + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum +{ + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Basic filter */ + +enum +{ + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr +{ + __u16 nmatches; + __u16 progid; +}; + +enum +{ + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr +{ + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum +{ + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +enum +{ + TCF_EM_CONTAINER, + TCF_EM_CMP, + TCF_EM_NBYTE, + TCF_EM_U32, + TCF_EM_META, + __TCF_EM_MAX +}; + +enum +{ + TCF_EM_PROG_TC +}; + +enum +{ + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h new file mode 100644 index 0000000..73d84c0 --- /dev/null +++ b/include/linux/pkt_sched.h @@ -0,0 +1,454 @@ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats +{ + __u64 bytes; /* NUmber of enqueues bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) + +struct tc_ratespec +{ + unsigned char cell_log; + unsigned char __reserved; + unsigned short feature; + short addend; + unsigned short mpu; + __u32 rate; +}; + +/* FIFO section */ + +struct tc_fifo_qopt +{ + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 + +struct tc_prio_qopt +{ + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* TBF section */ + +struct tc_tbf_qopt +{ + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum +{ + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt +{ + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +/* + * NOTE: limit, divisor and flows are hardwired to code at the moment. + * + * limit=flows=128, divisor=1024; + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. + */ + +/* RED section */ + +enum +{ + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +}; + +struct tc_red_xstats +{ + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum +{ + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +#define TCA_SET_OFF TCA_GRED_PARMS +struct tc_gred_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) +*/ + __u32 qth_min; /* Min average length threshold (bytes) +*/ + __u32 qth_max; /* Max average length threshold (bytes) +*/ + __u32 DP; /* upto 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; +/* gred setup */ +struct tc_gred_sopt +{ + __u32 DPs; + __u32 def_DP; + __u8 grio; +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt +{ + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob +{ + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shapped packets */ +}; +enum +{ + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats +{ + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 tokens; + __u32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt +{ + __u16 defcls; /* default class */ +}; + +struct tc_service_curve +{ + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats +{ + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum +{ + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt +{ + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt +{ + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl +{ + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u32 penalty; +}; + +struct tc_cbq_police +{ + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt +{ + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats +{ + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum +{ + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum +{ + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt +{ + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for delay all) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr +{ + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +#define NETEM_DIST_SCALE 8192 + +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h new file mode 100644 index 0000000..1facfe9 --- /dev/null +++ b/include/linux/rtnetlink.h @@ -0,0 +1,749 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include <linux/netlink.h> + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + RTM_GETPREFIX = 54, +#define RTM_GETPREFIX RTM_GETPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_MAX, +#define RTM_MAX RTM_MAX +}; + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg +{ + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum +{ + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t +{ + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t +{ + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + __RT_TABLE_MAX +}; +#define RT_TABLE_MAX (__RT_TABLE_MAX - 1) + + + +/* Routing message attributes */ + +enum rtattr_type_t +{ + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop +{ + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo +{ + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum +{ + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 + +struct rta_session +{ + __u8 proto; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + + +/********************************************************* + * Interface address. + ****/ + +struct ifaddrmsg +{ + unsigned char ifa_family; + unsigned char ifa_prefixlen; /* The prefix length */ + unsigned char ifa_flags; /* Flags */ + unsigned char ifa_scope; /* See above */ + int ifa_index; /* Link index */ +}; + +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ + +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +/* + Important comment: + IFA_ADDRESS is prefix address, rather than local interface address. + It makes no difference for normally configured broadcast interfaces, + but for point-to-point IFA_ADDRESS is DESTINATION address, + local address is supplied in IFA_LOCAL attribute. + */ + +/************************************************************** + * Neighbour discovery. + ****/ + +struct ndmsg +{ + unsigned char ndm_family; + unsigned char ndm_pad1; + unsigned short ndm_pad2; + int ndm_ifindex; /* Link index */ + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + + +struct nda_cacheinfo +{ + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg +{ + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg +{ + unsigned char prefix_family; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo +{ + __u32 preferred_time; + __u32 valid_time; +}; + +/* The struct should be in sync with struct net_device_stats */ +struct rtnl_link_stats +{ + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_bytes; /* total bytes received */ + __u32 tx_bytes; /* total bytes transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 collisions; + + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; /* receiver ring buff overflow */ + __u32 rx_crc_errors; /* recved pkt with crc error */ + __u32 rx_frame_errors; /* recv'd frame alignment error */ + __u32 rx_fifo_errors; /* recv'r fifo overrun */ + __u32 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap +{ + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +struct ifla_cacheinfo +{ + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg +{ + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum +{ + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_ACT_STATS, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + + +/* RTnetlink multicast groups */ + +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* TC action piece */ +struct tcamsg +{ + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/include/linux/tc_act/tc_gact.h b/include/linux/tc_act/tc_gact.h new file mode 100644 index 0000000..23a03eb --- /dev/null +++ b/include/linux/tc_act/tc_gact.h @@ -0,0 +1,34 @@ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_GACT 5 +struct tc_gact +{ + tc_gen; + +}; + +struct tc_gact_p +{ +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum +{ + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_ipt.h b/include/linux/tc_act/tc_ipt.h new file mode 100644 index 0000000..4b6f7b6 --- /dev/null +++ b/include/linux/tc_act/tc_ipt.h @@ -0,0 +1,21 @@ +#ifndef __LINUX_TC_IPT_H +#define __LINUX_TC_IPT_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_IPT 6 + +enum +{ + TCA_IPT_UNSPEC, + TCA_IPT_TABLE, + TCA_IPT_HOOK, + TCA_IPT_INDEX, + TCA_IPT_CNT, + TCA_IPT_TM, + TCA_IPT_TARG, + __TCA_IPT_MAX +}; +#define TCA_IPT_MAX (__TCA_IPT_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h new file mode 100644 index 0000000..71d6340 --- /dev/null +++ b/include/linux/tc_act/tc_mirred.h @@ -0,0 +1,28 @@ +#ifndef __LINUX_TC_MIR_H +#define __LINUX_TC_MIR_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_MIRRED 8 +#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/ +#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ +#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ +#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ + +struct tc_mirred +{ + tc_gen; + int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ + __u32 ifindex; /* ifindex of egress port */ +}; + +enum +{ + TCA_MIRRED_UNSPEC, + TCA_MIRRED_TM, + TCA_MIRRED_PARMS, + __TCA_MIRRED_MAX +}; +#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) + +#endif diff --git a/include/linux/tc_act/tc_pedit.h b/include/linux/tc_act/tc_pedit.h new file mode 100644 index 0000000..83e56e3 --- /dev/null +++ b/include/linux/tc_act/tc_pedit.h @@ -0,0 +1,36 @@ +#ifndef __LINUX_TC_PED_H +#define __LINUX_TC_PED_H + +#include <linux/pkt_cls.h> + +#define TCA_ACT_PEDIT 7 + +enum +{ + TCA_PEDIT_UNSPEC, + TCA_PEDIT_TM, + TCA_PEDIT_PARMS, + __TCA_PEDIT_MAX +}; +#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1) + +struct tc_pedit_key +{ + __u32 mask; /* AND */ + __u32 val; /*XOR */ + __u32 off; /*offset */ + __u32 at; + __u32 offmask; + __u32 shift; +}; + +struct tc_pedit_sel +{ + tc_gen; + unsigned char nkeys; + unsigned char flags; + struct tc_pedit_key keys[0]; +}; +#define tc_pedit tc_pedit_sel + +#endif diff --git a/include/linux/tcp.h b/include/linux/tcp.h new file mode 100644 index 0000000..9703d6b --- /dev/null +++ b/include/linux/tcp.h @@ -0,0 +1,194 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions for the TCP protocol. + * + * Version: @(#)tcp.h 1.0.2 04/28/93 + * + * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * + * 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. + */ +#ifndef _LINUX_TCP_H +#define _LINUX_TCP_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +struct tcphdr { + __u16 source; + __u16 dest; + __u32 seq; + __u32 ack_seq; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 res1:4, + doff:4, + fin:1, + syn:1, + rst:1, + psh:1, + ack:1, + urg:1, + ece:1, + cwr:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 doff:4, + res1:4, + cwr:1, + ece:1, + urg:1, + ack:1, + psh:1, + rst:1, + syn:1, + fin:1; +#else +#error "Adjust your <asm/byteorder.h> defines" +#endif + __u16 window; + __u16 check; + __u16 urg_ptr; +}; + + +enum { + TCP_ESTABLISHED = 1, + TCP_SYN_SENT, + TCP_SYN_RECV, + TCP_FIN_WAIT1, + TCP_FIN_WAIT2, + TCP_TIME_WAIT, + TCP_CLOSE, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, + TCP_LISTEN, + TCP_CLOSING, /* now a valid state */ + + TCP_MAX_STATES /* Leave at the end! */ +}; + +#define TCP_STATE_MASK 0xF +#define TCP_ACTION_FIN (1 << 7) + +enum { + TCPF_ESTABLISHED = (1 << 1), + TCPF_SYN_SENT = (1 << 2), + TCPF_SYN_RECV = (1 << 3), + TCPF_FIN_WAIT1 = (1 << 4), + TCPF_FIN_WAIT2 = (1 << 5), + TCPF_TIME_WAIT = (1 << 6), + TCPF_CLOSE = (1 << 7), + TCPF_CLOSE_WAIT = (1 << 8), + TCPF_LAST_ACK = (1 << 9), + TCPF_LISTEN = (1 << 10), + TCPF_CLOSING = (1 << 11) +}; + +/* + * The union cast uses a gcc extension to avoid aliasing problems + * (union is compatible to any of its members) + * This means this part of the code is -fstrict-aliasing safe now. + */ +union tcp_word_hdr { + struct tcphdr hdr; + __u32 words[5]; +}; + +#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) + +enum { + TCP_FLAG_CWR = __constant_htonl(0x00800000), + TCP_FLAG_ECE = __constant_htonl(0x00400000), + TCP_FLAG_URG = __constant_htonl(0x00200000), + TCP_FLAG_ACK = __constant_htonl(0x00100000), + TCP_FLAG_PSH = __constant_htonl(0x00080000), + TCP_FLAG_RST = __constant_htonl(0x00040000), + TCP_FLAG_SYN = __constant_htonl(0x00020000), + TCP_FLAG_FIN = __constant_htonl(0x00010000), + TCP_RESERVED_BITS = __constant_htonl(0x0F000000), + TCP_DATA_OFFSET = __constant_htonl(0xF0000000) +}; + +/* TCP socket options */ +#define TCP_NODELAY 1 /* Turn off Nagle's algorithm. */ +#define TCP_MAXSEG 2 /* Limit MSS */ +#define TCP_CORK 3 /* Never send partially complete segments */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Block/reenable quick acks */ + +#define TCPI_OPT_TIMESTAMPS 1 +#define TCPI_OPT_SACK 2 +#define TCPI_OPT_WSCALE 4 +#define TCPI_OPT_ECN 8 + +enum tcp_ca_state +{ + TCP_CA_Open = 0, +#define TCPF_CA_Open (1<<TCP_CA_Open) + TCP_CA_Disorder = 1, +#define TCPF_CA_Disorder (1<<TCP_CA_Disorder) + TCP_CA_CWR = 2, +#define TCPF_CA_CWR (1<<TCP_CA_CWR) + TCP_CA_Recovery = 3, +#define TCPF_CA_Recovery (1<<TCP_CA_Recovery) + TCP_CA_Loss = 4 +#define TCPF_CA_Loss (1<<TCP_CA_Loss) +}; + +struct tcp_info +{ + __u8 tcpi_state; + __u8 tcpi_ca_state; + __u8 tcpi_retransmits; + __u8 tcpi_probes; + __u8 tcpi_backoff; + __u8 tcpi_options; + __u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4; + + __u32 tcpi_rto; + __u32 tcpi_ato; + __u32 tcpi_snd_mss; + __u32 tcpi_rcv_mss; + + __u32 tcpi_unacked; + __u32 tcpi_sacked; + __u32 tcpi_lost; + __u32 tcpi_retrans; + __u32 tcpi_fackets; + + /* Times. */ + __u32 tcpi_last_data_sent; + __u32 tcpi_last_ack_sent; /* Not remembered, sorry. */ + __u32 tcpi_last_data_recv; + __u32 tcpi_last_ack_recv; + + /* Metrics. */ + __u32 tcpi_pmtu; + __u32 tcpi_rcv_ssthresh; + __u32 tcpi_rtt; + __u32 tcpi_rttvar; + __u32 tcpi_snd_ssthresh; + __u32 tcpi_snd_cwnd; + __u32 tcpi_advmss; + __u32 tcpi_reordering; + + __u32 tcpi_rcv_rtt; + __u32 tcpi_rcv_space; + + __u32 tcpi_total_retrans; +}; + + +#endif /* _LINUX_TCP_H */ diff --git a/include/linux/tcp_diag.h b/include/linux/tcp_diag.h new file mode 100644 index 0000000..ceee962 --- /dev/null +++ b/include/linux/tcp_diag.h @@ -0,0 +1,127 @@ +#ifndef _TCP_DIAG_H_ +#define _TCP_DIAG_H_ 1 + +/* Just some random number */ +#define TCPDIAG_GETSOCK 18 + +/* Socket identity */ +struct tcpdiag_sockid +{ + __u16 tcpdiag_sport; + __u16 tcpdiag_dport; + __u32 tcpdiag_src[4]; + __u32 tcpdiag_dst[4]; + __u32 tcpdiag_if; + __u32 tcpdiag_cookie[2]; +#define TCPDIAG_NOCOOKIE (~0U) +}; + +/* Request structure */ + +struct tcpdiagreq +{ + __u8 tcpdiag_family; /* Family of addresses. */ + __u8 tcpdiag_src_len; + __u8 tcpdiag_dst_len; + __u8 tcpdiag_ext; /* Query extended information */ + + struct tcpdiag_sockid id; + + __u32 tcpdiag_states; /* States to dump */ + __u32 tcpdiag_dbs; /* Tables to dump (NI) */ +}; + +enum +{ + TCPDIAG_REQ_NONE, + TCPDIAG_REQ_BYTECODE, +}; + +#define TCPDIAG_REQ_MAX TCPDIAG_REQ_BYTECODE + +/* Bytecode is sequence of 4 byte commands followed by variable arguments. + * All the commands identified by "code" are conditional jumps forward: + * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be + * length of the command and its arguments. + */ + +struct tcpdiag_bc_op +{ + unsigned char code; + unsigned char yes; + unsigned short no; +}; + +enum +{ + TCPDIAG_BC_NOP, + TCPDIAG_BC_JMP, + TCPDIAG_BC_S_GE, + TCPDIAG_BC_S_LE, + TCPDIAG_BC_D_GE, + TCPDIAG_BC_D_LE, + TCPDIAG_BC_AUTO, + TCPDIAG_BC_S_COND, + TCPDIAG_BC_D_COND, +}; + +struct tcpdiag_hostcond +{ + __u8 family; + __u8 prefix_len; + int port; + __u32 addr[0]; +}; + +/* Base info structure. It contains socket identity (addrs/ports/cookie) + * and, alas, the information shown by netstat. */ +struct tcpdiagmsg +{ + __u8 tcpdiag_family; + __u8 tcpdiag_state; + __u8 tcpdiag_timer; + __u8 tcpdiag_retrans; + + struct tcpdiag_sockid id; + + __u32 tcpdiag_expires; + __u32 tcpdiag_rqueue; + __u32 tcpdiag_wqueue; + __u32 tcpdiag_uid; + __u32 tcpdiag_inode; +}; + +/* Extensions */ + +enum +{ + TCPDIAG_NONE, + TCPDIAG_MEMINFO, + TCPDIAG_INFO, + TCPDIAG_VEGASINFO, +}; + +#define TCPDIAG_MAX TCPDIAG_VEGASINFO + + +/* TCPDIAG_MEM */ + +struct tcpdiag_meminfo +{ + __u32 tcpdiag_rmem; + __u32 tcpdiag_wmem; + __u32 tcpdiag_fmem; + __u32 tcpdiag_tmem; +}; + +/* TCPDIAG_VEGASINFO */ + +struct tcpvegas_info { + __u32 tcpv_enabled; + __u32 tcpv_rttcnt; + __u32 tcpv_rtt; + __u32 tcpv_minrtt; +}; + + +#endif /* _TCP_DIAG_H_ */ diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h new file mode 100644 index 0000000..f0df02a --- /dev/null +++ b/include/linux/xfrm.h @@ -0,0 +1,258 @@ +#ifndef _LINUX_XFRM_H +#define _LINUX_XFRM_H + +#include <linux/types.h> + +/* All of the structures in this file may not change size as they are + * passed into the kernel from userspace via netlink sockets. + */ + +/* Structure to encapsulate addresses. I do not want to use + * "standard" structure. My apologies. + */ +typedef union +{ + __u32 a4; + __u32 a6[4]; +} xfrm_address_t; + +/* Ident of a specific xfrm_state. It is used on input to lookup + * the state by (spi,daddr,ah/esp) or to store information about + * spi, protocol and tunnel address on output. + */ +struct xfrm_id +{ + xfrm_address_t daddr; + __u32 spi; + __u8 proto; +}; + +/* Selector, used as selector both on policy rules (SPD) and SAs. */ + +struct xfrm_selector +{ + xfrm_address_t daddr; + xfrm_address_t saddr; + __u16 dport; + __u16 dport_mask; + __u16 sport; + __u16 sport_mask; + __u16 family; + __u8 prefixlen_d; + __u8 prefixlen_s; + __u8 proto; + int ifindex; + uid_t user; +}; + +#define XFRM_INF (~(__u64)0) + +struct xfrm_lifetime_cfg +{ + __u64 soft_byte_limit; + __u64 hard_byte_limit; + __u64 soft_packet_limit; + __u64 hard_packet_limit; + __u64 soft_add_expires_seconds; + __u64 hard_add_expires_seconds; + __u64 soft_use_expires_seconds; + __u64 hard_use_expires_seconds; +}; + +struct xfrm_lifetime_cur +{ + __u64 bytes; + __u64 packets; + __u64 add_time; + __u64 use_time; +}; + +struct xfrm_replay_state +{ + __u32 oseq; + __u32 seq; + __u32 bitmap; +}; + +struct xfrm_algo { + char alg_name[64]; + int alg_key_len; /* in bits */ + char alg_key[0]; +}; + +struct xfrm_stats { + __u32 replay_window; + __u32 replay; + __u32 integrity_failed; +}; + +enum +{ + XFRM_POLICY_IN = 0, + XFRM_POLICY_OUT = 1, + XFRM_POLICY_FWD = 2, + XFRM_POLICY_MAX = 3 +}; + +enum +{ + XFRM_SHARE_ANY, /* No limitations */ + XFRM_SHARE_SESSION, /* For this session only */ + XFRM_SHARE_USER, /* For this user only */ + XFRM_SHARE_UNIQUE /* Use once */ +}; + +/* Netlink configuration messages. */ +enum { + XFRM_MSG_BASE = 0x10, + + XFRM_MSG_NEWSA = 0x10, +#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA + XFRM_MSG_DELSA, +#define XFRM_MSG_DELSA XFRM_MSG_DELSA + XFRM_MSG_GETSA, +#define XFRM_MSG_GETSA XFRM_MSG_GETSA + + XFRM_MSG_NEWPOLICY, +#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY + XFRM_MSG_DELPOLICY, +#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY + XFRM_MSG_GETPOLICY, +#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY + + XFRM_MSG_ALLOCSPI, +#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI + XFRM_MSG_ACQUIRE, +#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE + XFRM_MSG_EXPIRE, +#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE + + XFRM_MSG_UPDPOLICY, +#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY + XFRM_MSG_UPDSA, +#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA + + XFRM_MSG_POLEXPIRE, +#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE + + XFRM_MSG_FLUSHSA, +#define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA + XFRM_MSG_FLUSHPOLICY, +#define XFRM_MSG_FLUSHPOLICY XFRM_MSG_FLUSHPOLICY + + XFRM_MSG_MAX +}; + +struct xfrm_user_tmpl { + struct xfrm_id id; + __u16 family; + xfrm_address_t saddr; + __u32 reqid; + __u8 mode; + __u8 share; + __u8 optional; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; +}; + +struct xfrm_encap_tmpl { + __u16 encap_type; + __u16 encap_sport; + __u16 encap_dport; + xfrm_address_t encap_oa; +}; + +/* Netlink message attributes. */ +enum xfrm_attr_type_t { + XFRMA_UNSPEC, + XFRMA_ALG_AUTH, /* struct xfrm_algo */ + XFRMA_ALG_CRYPT, /* struct xfrm_algo */ + XFRMA_ALG_COMP, /* struct xfrm_algo */ + XFRMA_ENCAP, /* struct xfrm_algo + struct xfrm_encap_tmpl */ + XFRMA_TMPL, /* 1 or more struct xfrm_user_tmpl */ + __XFRMA_MAX + +#define XFRMA_MAX (__XFRMA_MAX - 1) +}; + +struct xfrm_usersa_info { + struct xfrm_selector sel; + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + struct xfrm_stats stats; + __u32 seq; + __u32 reqid; + __u16 family; + __u8 mode; /* 0=transport,1=tunnel */ + __u8 replay_window; + __u8 flags; +#define XFRM_STATE_NOECN 1 +#define XFRM_STATE_DECAP_DSCP 2 +}; + +struct xfrm_usersa_id { + xfrm_address_t daddr; + __u32 spi; + __u16 family; + __u8 proto; +}; + +struct xfrm_userspi_info { + struct xfrm_usersa_info info; + __u32 min; + __u32 max; +}; + +struct xfrm_userpolicy_info { + struct xfrm_selector sel; + struct xfrm_lifetime_cfg lft; + struct xfrm_lifetime_cur curlft; + __u32 priority; + __u32 index; + __u8 dir; + __u8 action; +#define XFRM_POLICY_ALLOW 0 +#define XFRM_POLICY_BLOCK 1 + __u8 flags; +#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */ + __u8 share; +}; + +struct xfrm_userpolicy_id { + struct xfrm_selector sel; + __u32 index; + __u8 dir; +}; + +struct xfrm_user_acquire { + struct xfrm_id id; + xfrm_address_t saddr; + struct xfrm_selector sel; + struct xfrm_userpolicy_info policy; + __u32 aalgos; + __u32 ealgos; + __u32 calgos; + __u32 seq; +}; + +struct xfrm_user_expire { + struct xfrm_usersa_info state; + __u8 hard; +}; + +struct xfrm_user_polexpire { + struct xfrm_userpolicy_info pol; + __u8 hard; +}; + +struct xfrm_usersa_flush { + __u8 proto; +}; + +#define XFRMGRP_ACQUIRE 1 +#define XFRMGRP_EXPIRE 2 + +#endif /* _LINUX_XFRM_H */ diff --git a/include/ll_map.h b/include/ll_map.h new file mode 100644 index 0000000..3bff5e9 --- /dev/null +++ b/include/ll_map.h @@ -0,0 +1,13 @@ +#ifndef __LL_MAP_H__ +#define __LL_MAP_H__ 1 + +extern int ll_remember_index(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int ll_init_map(struct rtnl_handle *rth); +extern int ll_name_to_index(const char *name); +extern const char *ll_index_to_name(int idx); +extern const char *ll_idx_n2a(int idx, char *buf); +extern int ll_index_to_type(int idx); +extern unsigned ll_index_to_flags(int idx); + +#endif /* __LL_MAP_H__ */ diff --git a/include/rt_names.h b/include/rt_names.h new file mode 100644 index 0000000..249231e --- /dev/null +++ b/include/rt_names.h @@ -0,0 +1,30 @@ +#ifndef RT_NAMES_H_ +#define RT_NAMES_H_ 1 + +#include <asm/types.h> + +char* rtnl_rtprot_n2a(int id, char *buf, int len); +char* rtnl_rtscope_n2a(int id, char *buf, int len); +char* rtnl_rttable_n2a(int id, char *buf, int len); +char* rtnl_rtrealm_n2a(int id, char *buf, int len); +char* rtnl_dsfield_n2a(int id, char *buf, int len); +int rtnl_rtprot_a2n(__u32 *id, char *arg); +int rtnl_rtscope_a2n(__u32 *id, char *arg); +int rtnl_rttable_a2n(__u32 *id, char *arg); +int rtnl_rtrealm_a2n(__u32 *id, char *arg); +int rtnl_dsfield_a2n(__u32 *id, char *arg); + +const char *inet_proto_n2a(int proto, char *buf, int len); +int inet_proto_a2n(char *buf); + + +const char * ll_type_n2a(int type, char *buf, int len); + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen); +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg); + +const char * ll_proto_n2a(unsigned short id, char *buf, int len); +int ll_proto_a2n(unsigned short *id, char *buf); + + +#endif diff --git a/include/rtm_map.h b/include/rtm_map.h new file mode 100644 index 0000000..70bda7d --- /dev/null +++ b/include/rtm_map.h @@ -0,0 +1,10 @@ +#ifndef __RTM_MAP_H__ +#define __RTM_MAP_H__ 1 + +char *rtnl_rtntype_n2a(int id, char *buf, int len); +int rtnl_rtntype_a2n(int *id, char *arg); + +int get_rt_realms(__u32 *realms, char *arg); + + +#endif /* __RTM_MAP_H__ */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..906e394 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,126 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ 1 + +#include <asm/types.h> +#include <resolv.h> + +#include "libnetlink.h" +#include "ll_map.h" +#include "rtm_map.h" + +extern int preferred_family; +extern int show_stats; +extern int show_details; +extern int show_raw; +extern int resolve_hosts; +extern int oneline; +extern char * _SL_; + +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#ifndef IPPROTO_COMP +#define IPPROTO_COMP 108 +#endif +#ifndef IPSEC_PROTO_ANY +#define IPSEC_PROTO_ANY 255 +#endif + +#define SPRINT_BSIZE 64 +#define SPRINT_BUF(x) char x[SPRINT_BSIZE] + +extern void incomplete_command(void) __attribute__((noreturn)); + +#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) +#define NEXT_ARG_OK() (argc - 1 > 0) +#define PREV_ARG() do { argv--; argc++; } while(0) + +typedef struct +{ + __u8 family; + __u8 bytelen; + __s16 bitlen; + __u32 data[4]; +} inet_prefix; + +#define DN_MAXADDL 20 +#ifndef AF_DECnet +#define AF_DECnet 12 +#endif + +struct dn_naddr +{ + unsigned short a_len; + unsigned char a_addr[DN_MAXADDL]; +}; + +#define IPX_NODE_LEN 6 + +struct ipx_addr { + u_int32_t ipx_net; + u_int8_t ipx_node[IPX_NODE_LEN]; +}; + +extern __u32 get_addr32(const char *name); +extern int get_addr_1(inet_prefix *dst, const char *arg, int family); +extern int get_prefix_1(inet_prefix *dst, char *arg, int family); +extern int get_addr(inet_prefix *dst, const char *arg, int family); +extern int get_prefix(inet_prefix *dst, char *arg, int family); + +extern int get_integer(int *val, const char *arg, int base); +extern int get_unsigned(unsigned *val, const char *arg, int base); +#define get_byte get_u8 +#define get_ushort get_u16 +#define get_short get_s16 +extern int get_u64(__u64 *val, const char *arg, int base); +extern int get_u32(__u32 *val, const char *arg, int base); +extern int get_u16(__u16 *val, const char *arg, int base); +extern int get_s16(__s16 *val, const char *arg, int base); +extern int get_u8(__u8 *val, const char *arg, int base); +extern int get_s8(__s8 *val, const char *arg, int base); + +extern __u8* hexstring_n2a(const __u8 *str, int len, __u8 *buf, int blen); +extern __u8* hexstring_a2n(const __u8 *str, __u8 *buf, int blen); + +extern const char *format_host(int af, int len, const void *addr, + char *buf, int buflen); +extern const char *rt_addr_n2a(int af, int len, const void *addr, + char *buf, int buflen); + +void missarg(const char *) __attribute__((noreturn)); +void invarg(const char *, const char *) __attribute__((noreturn)); +void duparg(const char *, const char *) __attribute__((noreturn)); +void duparg2(const char *, const char *) __attribute__((noreturn)); +int matches(const char *arg, const char *pattern); +extern int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits); + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len); +int dnet_pton(int af, const char *src, void *addr); + +const char *ipx_ntop(int af, const void *addr, char *str, size_t len); +int ipx_pton(int af, const char *src, void *addr); + +extern int __iproute2_hz_internal; +extern int __get_hz(void); + +static __inline__ int get_hz(void) +{ + if (__iproute2_hz_internal == 0) + __iproute2_hz_internal = __get_hz(); + return __iproute2_hz_internal; +} + +extern int __iproute2_user_hz_internal; +extern int __get_user_hz(void); + +static __inline__ int get_user_hz(void) +{ + if (__iproute2_user_hz_internal == 0) + __iproute2_user_hz_internal = __get_user_hz(); + return __iproute2_user_hz_internal; +} + +#endif /* __UTILS_H__ */ diff --git a/ip/Makefile b/ip/Makefile new file mode 100644 index 0000000..bcc419b --- /dev/null +++ b/ip/Makefile @@ -0,0 +1,24 @@ +IPOBJ=ip.o ipaddress.o iproute.o iprule.o \ + rtm_map.o iptunnel.o ipneigh.o iplink.o \ + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ + ipxfrm.o xfrm_state.o xfrm_policy.o + +RTMONOBJ=rtmon.o + +ALLOBJ=$(IPOBJ) $(RTMONOBJ) +SCRIPTS=ifcfg rtpr routel routef +TARGETS=ip rtmon + +all: $(TARGETS) $(SCRIPTS) + +ip: $(IPOBJ) $(LIBNETLINK) $(LIBUTIL) + +rtmon: $(RTMONOBJ) $(LIBNETLINK) + +install: all + install -m 0755 -s $(TARGETS) $(DESTDIR)$(SBINDIR) + install -m 0755 $(SCRIPTS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(ALLOBJ) $(TARGETS) + diff --git a/ip/ifcfg b/ip/ifcfg new file mode 100755 index 0000000..ed6960f --- /dev/null +++ b/ip/ifcfg @@ -0,0 +1,145 @@ +#! /bin/bash + +CheckForwarding () { + local sbase fwd + sbase=/proc/sys/net/ipv4/conf + fwd=0 + if [ -d $sbase ]; then + for dir in $sbase/*/forwarding; do + fwd=$[$fwd + `cat $dir`] + done + else + fwd=2 + fi + return $fwd +} + +RestartRDISC () { + killall -HUP rdisc || rdisc -fs +} + +ABCMaskLen () { + local class; + + class=${1%%.*} + if [ "$1" = "" -o $class -eq 0 -o $class -ge 224 ]; then return 0 + elif [ $class -ge 224 ]; then return 0 + elif [ $class -ge 192 ]; then return 24 + elif [ $class -ge 128 ]; then return 16 + else return 8; fi +} + +label="label $1" +ldev="$1" +dev=${1%:*} +if [ "$dev" = "" -o "$1" = "help" ]; then + echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2 + echo " add - add new address" 1>&2 + echo " del - delete address" 1>&2 + echo " stop - completely disable IP" 1>&2 + exit 1 +fi +shift + +CheckForwarding +fwd=$? +if [ $fwd -ne 0 ]; then + echo "Forwarding is ON or its state is unknown ($fwd). OK, No RDISC." 1>&2 +fi + + +deleting=0 +case "$1" in +add) shift ;; +stop) + if [ "$ldev" != "$dev" ]; then + echo "Cannot stop alias $ldev" 1>&2 + exit 1; + fi + ip -4 addr flush dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 ;; +del*) + deleting=1; shift ;; +*) +esac + +ipaddr= +pfxlen= +if [ "$1" != "" ]; then + ipaddr=${1%/*} + if [ "$1" != "$ipaddr" ]; then + pfxlen=${1#*/} + fi + if [ "$ipaddr" = "" ]; then + echo "$1 is bad IP address." 1>&2 + exit 1 + fi +fi +shift + +peer=$1 +if [ "$peer" != "" ]; then + if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then + echo "Peer address with non-trivial netmask." 1>&2 + exit 1 + fi + pfx="$ipaddr peer $peer" +else + if [ "$pfxlen" = "" ]; then + ABCMaskLen $ipaddr + pfxlen=$? + fi + pfx="$ipaddr/$pfxlen" +fi + +if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then + label= +fi + +if [ $deleting -ne 0 ]; then + ip addr del $pfx dev $dev $label || exit 1 + if [ $fwd -eq 0 ]; then RestartRDISC; fi + exit 0 +fi + + +if ! ip link set up dev $dev ; then + echo "Error: cannot enable interface $dev." 1>&2 + exit 1 +fi +if [ "$ipaddr" = "" ]; then exit 0; fi + +if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then + echo "Error: some host already uses address $ipaddr on $dev." 1>&2 + exit 1 +fi + +if ! ip address add $pfx brd + dev $dev $label; then + echo "Error: failed to add $pfx on $dev." 1>&2 + exit 1 +fi + +arping -q -A -c 1 -I $dev $ipaddr +noarp=$? +( sleep 2 ; + arping -q -U -c 1 -I $dev $ipaddr ) >& /dev/null </dev/null & + +ip route add unreachable 224.0.0.0/24 >& /dev/null +ip route add unreachable 255.255.255.255 >& /dev/null +if [ `ip link ls $dev | grep -c MULTICAST` -ge 1 ]; then + ip route add 224.0.0.0/4 dev $dev scope global >& /dev/null +fi + +if [ $fwd -eq 0 ]; then + if [ $noarp -eq 0 ]; then + ip ro append default dev $dev metric 30000 scope global + elif [ "$peer" != "" ]; then + if ping -q -c 2 -w 4 $peer ; then + ip ro append default via $peer dev $dev metric 30001 + fi + fi + RestartRDISC +fi + +exit 0 diff --git a/ip/ip b/ip/ip new file mode 100755 index 0000000000000000000000000000000000000000..5ba7f4e098021fc7c776572b0e7252b685e7fa4c GIT binary patch literal 153676 zcmc${3wTsT5;i;u84U`Ypo9%<(13%2Hxh0OLNo)(I?<>opsaWS2@w<l!wd#R2+oW$ zj=`w7;vHRG(bW}Qg%EKjBtQUf5iiS%3UZNij0lPb(7=4}TYb)%$;h(b|3Clp{G;KV zuBxuCuI{d`zMlC-C4Wv$NpV=eRL6x5hU&T&2uS_wa4hMXDq;Rp;27da$G<%s-5n<& z>=YO=#1*u8)|srWXS!e=j&^va@jcZ9q!y?b^O>ze4(n-2$S2c_{|d}k^&I}CqO_h4 zM~2`DrL2zaCa3LJ3fy5nb8=O%`XcqT;!!-t>tn|2W71pCr6xP;Y2}Z3<6pL!zwFck zgcvU1!;vHZ#-E}Kz((!%mh#=TQZaU|bPJ{R%$7`mkNLFXU5R)m$mD$dhb3pfX;W_+ zJh0!i((|TGo$jkRuVTpH^9Bz*f7Xoi`-}Y49woW>^0C5@*|E|jpf~;~VMqL3h`&GL zkH7QO!!gW!7T`%<J@D5Lf7Wj(Ua0ax_&WoCL-02Me<$Ow5B>(?FCTw>@%IP(or1q! z_`3jq!|`__{`fl|f8FqRHvT%`&&nTO|6lz&nXy<;`Lps&U1SO*{*Pw9o{Fbg+yXf% zP9kqv`1dsYbvNNIJbRk&r<>2S@H_{9tmCZvIrz)NUwiy@!5_<*I_K|P{B^}&X9n<N z74DC}!T389e}BT?fBU6FQ0mh99WY-`MFtb$l!w1%N$?g0c6^FwBK;30+?hmwIl}yX zngkChu;Udx6Zw2^!gnUY7n$&PlHgyN@S{oaG_yb+OM;(j7HD~rJWEVIhmzpWm=)oZ zB=}H-`5Tx7f6;`$i)Uv?mLn(QDxtIL<t<6{mL9q%!EcpVs543BWti|yN%S*J_~0aZ zs~jInqVJ6`e+!b}V-(m?glA&8d}+c*Ced5vaxjU0qsf1N61>dh^IQ@<WWv8og5PTL z`DaqPy-fNmlISg&Z%m@6ec-PIe-;hHv>E)Jmqh=G0z1x6(x+8k_b1U?dbk$x68Yb2 z@+nNBUuwc1OM+W%y4`<Cw?2u#RZn^*(LWEq{5_Y%|0e}@+?fP_(S#36g3|``w>AmB zNr4@GlG3$YiUUdX>rDCzJQMYEp9#M-iQcNWZzsVmJ+q!B^0DX#CDG3``G1iFr;X*W zISD=-VgC9i<?9j?o|*(dNs¨_<}06FwEsM0hYmA#TTm_MuObeefsITjk4WIrvNd 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 literal 0 HcmV?d00001 diff --git a/ip/ip.c b/ip/ip.c new file mode 100644 index 0000000..6358ec4 --- /dev/null +++ b/ip/ip.c @@ -0,0 +1,171 @@ +/* + * ip.c "ip" utility frontend. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "ip_common.h" + +int preferred_family = AF_UNSPEC; +int show_stats = 0; +int resolve_hosts = 0; +int oneline = 0; +char * _SL_ = NULL; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" +"where OBJECT := { link | addr | route | rule | neigh | tunnel |\n" +" maddr | mroute | monitor | xfrm }\n" +" OPTIONS := { -V[ersion] | -s[tatistics] | -r[esolve] |\n" +" -f[amily] { inet | inet6 | ipx | dnet | link } | -o[neline] }\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + char *basename; + + basename = strrchr(argv[0], '/'); + if (basename == NULL) + basename = argv[0]; + else + basename++; + + while (argc > 1) { + char *opt = argv[1]; + if (strcmp(opt,"--") == 0) { + argc--; argv++; + break; + } + if (opt[0] != '-') + break; + if (opt[1] == '-') + opt++; + if (matches(opt, "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(argv[1], "dnet") == 0) + preferred_family = AF_DECnet; + else if (strcmp(argv[1], "link") == 0) + preferred_family = AF_PACKET; + else if (strcmp(argv[1], "ipx") == 0) + preferred_family = AF_IPX; + else if (strcmp(argv[1], "help") == 0) + usage(); + else + invarg(argv[1], "invalid protocol family"); + } else if (strcmp(opt, "-4") == 0) { + preferred_family = AF_INET; + } else if (strcmp(opt, "-6") == 0) { + preferred_family = AF_INET6; + } else if (strcmp(opt, "-0") == 0) { + preferred_family = AF_PACKET; + } else if (strcmp(opt, "-I") == 0) { + preferred_family = AF_IPX; + } else if (strcmp(opt, "-D") == 0) { + preferred_family = AF_DECnet; + } else if (matches(opt, "-stats") == 0 || + matches(opt, "-statistics") == 0) { + ++show_stats; + } else if (matches(opt, "-resolve") == 0) { + ++resolve_hosts; + } else if (matches(opt, "-oneline") == 0) { + ++oneline; +#if 0 + } else if (matches(opt, "-numeric") == 0) { + rtnl_names_numeric++; +#endif + } else if (matches(opt, "-Version") == 0) { + printf("ip utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + } else if (matches(opt, "-help") == 0) { + usage(); + } else { + fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt); + exit(-1); + } + argc--; argv++; + } + + _SL_ = oneline ? "\\" : "\n" ; + + if (strcmp(basename, "ipaddr") == 0) + return do_ipaddr(argc-1, argv+1); + if (strcmp(basename, "ipmaddr") == 0) + return do_multiaddr(argc-1, argv+1); + if (strcmp(basename, "iproute") == 0) + return do_iproute(argc-1, argv+1); + if (strcmp(basename, "iprule") == 0) + return do_iprule(argc-1, argv+1); + if (strcmp(basename, "ipneigh") == 0) + return do_ipneigh(argc-1, argv+1); + if (strcmp(basename, "iplink") == 0) + return do_iplink(argc-1, argv+1); + if (strcmp(basename, "iptunnel") == 0) + return do_iptunnel(argc-1, argv+1); + if (strcmp(basename, "ipmonitor") == 0) + return do_ipmonitor(argc-1, argv+1); + if (strcmp(basename, "ipxfrm") == 0) + return do_xfrm(argc-1, argv+1); + + if (argc > 1) { + if (matches(argv[1], "address") == 0) + return do_ipaddr(argc-2, argv+2); + if (matches(argv[1], "maddress") == 0) + return do_multiaddr(argc-2, argv+2); + if (matches(argv[1], "route") == 0) + return do_iproute(argc-2, argv+2); + if (matches(argv[1], "rule") == 0) + return do_iprule(argc-2, argv+2); + if (matches(argv[1], "mroute") == 0) + return do_multiroute(argc-2, argv+2); + if (matches(argv[1], "neighbor") == 0 || + matches(argv[1], "neighbour") == 0) + return do_ipneigh(argc-2, argv+2); + if (matches(argv[1], "link") == 0) + return do_iplink(argc-2, argv+2); + if (matches(argv[1], "tunnel") == 0 || + strcmp(argv[1], "tunl") == 0) + return do_iptunnel(argc-2, argv+2); + if (matches(argv[1], "monitor") == 0) + return do_ipmonitor(argc-2, argv+2); + if (matches(argv[1], "xfrm") == 0) + return do_xfrm(argc-2, argv+2); + if (matches(argv[1], "help") == 0) + usage(); + fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv[1]); + exit(-1); + } + usage(); +} diff --git a/ip/ip.o b/ip/ip.o new file mode 100644 index 0000000000000000000000000000000000000000..61cd6c43db4fd999e0f3ff5f9ce2fb8527f014f7 GIT binary patch literal 6928 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 literal 0 HcmV?d00001 diff --git a/ip/ip_common.h b/ip/ip_common.h new file mode 100644 index 0000000..688d384 --- /dev/null +++ b/ip/ip_common.h @@ -0,0 +1,29 @@ +extern int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg); +extern int print_addrinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg); +extern int print_neigh(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int ipaddr_list(int argc, char **argv); +extern int ipaddr_list_link(int argc, char **argv); +extern int iproute_monitor(int argc, char **argv); +extern void iplink_usage(void) __attribute__((noreturn)); +extern void iproute_reset_filter(void); +extern void ipaddr_reset_filter(int); +extern void ipneigh_reset_filter(void); +extern int print_route(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int print_prefix(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg); +extern int do_ipaddr(int argc, char **argv); +extern int do_iproute(int argc, char **argv); +extern int do_iprule(int argc, char **argv); +extern int do_ipneigh(int argc, char **argv); +extern int do_iptunnel(int argc, char **argv); +extern int do_iplink(int argc, char **argv); +extern int do_ipmonitor(int argc, char **argv); +extern int do_multiaddr(int argc, char **argv); +extern int do_multiroute(int argc, char **argv); +extern int do_xfrm(int argc, char **argv); diff --git a/ip/ipaddress.c b/ip/ipaddress.c new file mode 100644 index 0000000..92f0089 --- /dev/null +++ b/ip/ipaddress.c @@ -0,0 +1,904 @@ +/* + * ipaddress.c "ip address". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Changes: + * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <fnmatch.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" +#include "ll_map.h" +#include "ip_common.h" + +static struct +{ + int ifindex; + int family; + int oneline; + int showqueue; + inet_prefix pfx; + int scope, scopemask; + int flags, flagmask; + int up; + char *label; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +static int do_link; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + if (do_link) { + iplink_usage(); + } + fprintf(stderr, "Usage: ip addr {add|del} IFADDR dev STRING\n"); + fprintf(stderr, " ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n"); + fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n"); + fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n"); + fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n"); + fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n"); + fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); + fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n"); + fprintf(stderr, " tentative | deprecated ]\n"); + exit(-1); +} + +void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) +{ + fprintf(fp, "<"); + if (flags & IFF_UP && !(flags & IFF_RUNNING)) + fprintf(fp, "NO-CARRIER%s", flags ? "," : ""); + flags &= ~IFF_RUNNING; +#define _PF(f) if (flags&IFF_##f) { \ + flags &= ~IFF_##f ; \ + fprintf(fp, #f "%s", flags ? "," : ""); } + _PF(LOOPBACK); + _PF(BROADCAST); + _PF(POINTOPOINT); + _PF(MULTICAST); + _PF(NOARP); + _PF(ALLMULTI); + _PF(PROMISC); + _PF(MASTER); + _PF(SLAVE); + _PF(DEBUG); + _PF(DYNAMIC); + _PF(AUTOMEDIA); + _PF(PORTSEL); + _PF(NOTRAILERS); + _PF(UP); +#undef _PF + if (flags) + fprintf(fp, "%x", flags); + if (mdown) + fprintf(fp, ",M-DOWN"); + fprintf(fp, "> "); +} + +void print_queuelen(char *name) +{ + struct ifreq ifr; + int s; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { + perror("SIOCGIFXQLEN"); + close(s); + return; + } + close(s); + + if (ifr.ifr_qlen) + printf("qlen %d", ifr.ifr_qlen); +} + +int print_linkinfo(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + int len = n->nlmsg_len; + unsigned m_flag = 0; + + if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return 0; + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL) { + fprintf(stderr, "BUG: nil ifname\n"); + return -1; + } + if (filter.label && + (!filter.family || filter.family == AF_PACKET) && + fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) + return 0; + + if (n->nlmsg_type == RTM_DELLINK) + fprintf(fp, "Deleted "); + + fprintf(fp, "%d: %s", ifi->ifi_index, + tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); + + if (tb[IFLA_LINK]) { + SPRINT_BUF(b1); + int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); + if (iflink == 0) + fprintf(fp, "@NONE: "); + else { + fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + m_flag = ll_index_to_flags(iflink); + m_flag = !(m_flag & IFF_UP); + } + } else { + fprintf(fp, ": "); + } + print_link_flags(fp, ifi->ifi_flags, m_flag); + + if (tb[IFLA_MTU]) + 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 (filter.showqueue) + print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); + + if (!filter.family || filter.family == AF_PACKET) { + SPRINT_BUF(b1); + fprintf(fp, "%s", _SL_); + fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); + + if (tb[IFLA_ADDRESS]) { + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), + RTA_PAYLOAD(tb[IFLA_ADDRESS]), + ifi->ifi_type, + b1, sizeof(b1))); + } + if (tb[IFLA_BROADCAST]) { + if (ifi->ifi_flags&IFF_POINTOPOINT) + fprintf(fp, " peer "); + else + fprintf(fp, " brd "); + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), + RTA_PAYLOAD(tb[IFLA_BROADCAST]), + ifi->ifi_type, + b1, sizeof(b1))); + } + } + if (do_link && tb[IFLA_STATS] && show_stats) { + struct rtnl_link_stats slocal; + struct rtnl_link_stats *s = RTA_DATA(tb[IFLA_STATS]); + if (((unsigned long)s) & (sizeof(unsigned long)-1)) { + memcpy(&slocal, s, sizeof(slocal)); + s = &slocal; + } + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", + s->rx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", + s->rx_bytes, s->rx_packets, s->rx_errors, + s->rx_dropped, s->rx_over_errors, + s->multicast + ); + if (s->rx_compressed) + fprintf(fp, " %-7u", s->rx_compressed); + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + fprintf(fp, " %-7u %-7u %-7u %-7u %-7u", + s->rx_length_errors, + s->rx_crc_errors, + s->rx_frame_errors, + s->rx_fifo_errors, + s->rx_missed_errors + ); + } + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", + s->tx_compressed ? "compressed" : "", _SL_); + fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", + s->tx_bytes, s->tx_packets, s->tx_errors, + s->tx_dropped, s->tx_carrier_errors, s->collisions); + if (s->tx_compressed) + fprintf(fp, " %-7u", s->tx_compressed); + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX errors: aborted fifo window heartbeat%s", _SL_); + fprintf(fp, " %-7u %-7u %-7u %-7u", + s->tx_aborted_errors, + s->tx_fifo_errors, + s->tx_window_errors, + s->tx_heartbeat_errors + ); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * rta_tb[IFA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) + return 0; + len -= NLMSG_LENGTH(sizeof(*ifa)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) + return 0; + + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + if (!rta_tb[IFA_LOCAL]) + rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; + if (!rta_tb[IFA_ADDRESS]) + rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; + + if (filter.ifindex && filter.ifindex != ifa->ifa_index) + return 0; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + return 0; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + return 0; + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (rta_tb[IFA_LABEL]) + label = RTA_DATA(rta_tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + return 0; + } + if (filter.pfx.family) { + if (rta_tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + + if (filter.family && filter.family != ifa->ifa_family) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELADDR; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELADDR) + fprintf(fp, "Deleted "); + + if (filter.oneline || filter.flushb) + fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); + if (ifa->ifa_family == AF_INET) + fprintf(fp, " inet "); + else if (ifa->ifa_family == AF_INET6) + fprintf(fp, " inet6 "); + else if (ifa->ifa_family == AF_DECnet) + fprintf(fp, " dnet "); + else if (ifa->ifa_family == AF_IPX) + fprintf(fp, " ipx "); + else + fprintf(fp, " family %d ", ifa->ifa_family); + + if (rta_tb[IFA_LOCAL]) { + fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_LOCAL]), + RTA_DATA(rta_tb[IFA_LOCAL]), + abuf, sizeof(abuf))); + + if (rta_tb[IFA_ADDRESS] == NULL || + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { + fprintf(fp, "/%d ", ifa->ifa_prefixlen); + } else { + fprintf(fp, " peer %s/%d ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), + RTA_DATA(rta_tb[IFA_ADDRESS]), + abuf, sizeof(abuf)), + ifa->ifa_prefixlen); + } + } + + if (rta_tb[IFA_BROADCAST]) { + fprintf(fp, "brd %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), + RTA_DATA(rta_tb[IFA_BROADCAST]), + abuf, sizeof(abuf))); + } + if (rta_tb[IFA_ANYCAST]) { + fprintf(fp, "any %s ", + rt_addr_n2a(ifa->ifa_family, + RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), + RTA_DATA(rta_tb[IFA_ANYCAST]), + abuf, sizeof(abuf))); + } + fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + if (ifa->ifa_flags&IFA_F_SECONDARY) { + ifa->ifa_flags &= ~IFA_F_SECONDARY; + fprintf(fp, "secondary "); + } + if (ifa->ifa_flags&IFA_F_TENTATIVE) { + ifa->ifa_flags &= ~IFA_F_TENTATIVE; + fprintf(fp, "tentative "); + } + if (ifa->ifa_flags&IFA_F_DEPRECATED) { + ifa->ifa_flags &= ~IFA_F_DEPRECATED; + fprintf(fp, "deprecated "); + } + if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { + fprintf(fp, "dynamic "); + } else + ifa->ifa_flags &= ~IFA_F_PERMANENT; + if (ifa->ifa_flags) + fprintf(fp, "flags %02x ", ifa->ifa_flags); + if (rta_tb[IFA_LABEL]) + fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); + if (rta_tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); + char buf[128]; + fprintf(fp, "%s", _SL_); + if (ci->ifa_valid == 0xFFFFFFFFU) + sprintf(buf, "valid_lft forever"); + else + sprintf(buf, "valid_lft %dsec", ci->ifa_valid); + if (ci->ifa_prefered == 0xFFFFFFFFU) + sprintf(buf+strlen(buf), " preferred_lft forever"); + else + sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); + fprintf(fp, " %s", buf); + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +struct nlmsg_list +{ + struct nlmsg_list *next; + struct nlmsghdr h; +}; + +int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) +{ + for ( ;ainfo ; ainfo = ainfo->next) { + struct nlmsghdr *n = &ainfo->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (n->nlmsg_type != RTM_NEWADDR) + continue; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) + return -1; + + if (ifa->ifa_index != ifindex || + (filter.family && filter.family != ifa->ifa_family)) + continue; + + print_addrinfo(NULL, n, fp); + } + return 0; +} + + +static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct nlmsg_list **linfo = (struct nlmsg_list**)arg; + struct nlmsg_list *h; + struct nlmsg_list **lp; + + h = malloc(n->nlmsg_len+sizeof(void*)); + if (h == NULL) + return -1; + + memcpy(&h->h, n, n->nlmsg_len); + h->next = NULL; + + for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; + *lp = h; + + ll_remember_index(who, n, NULL); + return 0; +} + +int ipaddr_list_or_flush(int argc, char **argv, int flush) +{ + struct nlmsg_list *linfo = NULL; + struct nlmsg_list *ainfo = NULL; + struct nlmsg_list *l; + struct rtnl_handle rth; + char *filter_dev = NULL; + int no_link = 0; + + ipaddr_reset_filter(oneline); + filter.showqueue = 1; + + if (filter.family == AF_UNSPEC) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + if (filter.family == AF_PACKET) { + fprintf(stderr, "Cannot flush link addresses.\n"); + return -1; + } + } + + while (argc > 0) { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (strcmp(*argv, "up") == 0) { + filter.up = 1; + } else if (strcmp(*argv, "dynamic") == 0) { + filter.flags &= ~IFA_F_PERMANENT; + filter.flagmask |= IFA_F_PERMANENT; + } else if (strcmp(*argv, "permanent") == 0) { + filter.flags |= IFA_F_PERMANENT; + filter.flagmask |= IFA_F_PERMANENT; + } else if (strcmp(*argv, "secondary") == 0) { + filter.flags |= IFA_F_SECONDARY; + filter.flagmask |= IFA_F_SECONDARY; + } else if (strcmp(*argv, "primary") == 0) { + filter.flags &= ~IFA_F_SECONDARY; + filter.flagmask |= IFA_F_SECONDARY; + } else if (strcmp(*argv, "tentative") == 0) { + filter.flags |= IFA_F_TENTATIVE; + filter.flagmask |= IFA_F_TENTATIVE; + } else if (strcmp(*argv, "deprecated") == 0) { + filter.flags |= IFA_F_DEPRECATED; + filter.flagmask |= IFA_F_DEPRECATED; + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + filter.label = *argv; + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (filter_dev) + duparg2("dev", *argv); + filter_dev = *argv; + } + argv++; argc--; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + if (filter_dev) { + filter.ifindex = ll_name_to_index(filter_dev); + if (filter.ifindex <= 0) { + fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev); + return -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + if (show_stats) { + printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.family != AF_PACKET) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + + if (filter.family && filter.family != AF_PACKET) { + struct nlmsg_list **lp; + lp=&linfo; + + if (filter.oneline) + no_link = 1; + + while ((l=*lp)!=NULL) { + int ok = 0; + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + struct nlmsg_list *a; + + for (a=ainfo; a; a=a->next) { + struct nlmsghdr *n = &a->h; + struct ifaddrmsg *ifa = NLMSG_DATA(n); + + if (ifa->ifa_index != ifi->ifi_index || + (filter.family && filter.family != ifa->ifa_family)) + continue; + if ((filter.scope^ifa->ifa_scope)&filter.scopemask) + continue; + if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + continue; + if (filter.pfx.family || filter.label) { + struct rtattr *tb[IFA_MAX+1]; + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + if (!tb[IFA_LOCAL]) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + + if (filter.pfx.family && tb[IFA_LOCAL]) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = ifa->ifa_family; + memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + continue; + } + if (filter.label) { + SPRINT_BUF(b1); + const char *label; + if (tb[IFA_LABEL]) + label = RTA_DATA(tb[IFA_LABEL]); + else + label = ll_idx_n2a(ifa->ifa_index, b1); + if (fnmatch(filter.label, label, 0) != 0) + continue; + } + } + + ok = 1; + break; + } + if (!ok) + *lp = l->next; + else + lp = &l->next; + } + } + + for (l=linfo; l; l = l->next) { + if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { + struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + if (filter.family != AF_PACKET) + print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); + } + fflush(stdout); + } + + exit(0); +} + +int ipaddr_list_link(int argc, char **argv) +{ + preferred_family = AF_PACKET; + do_link = 1; + return ipaddr_list_or_flush(argc, argv, 0); +} + +void ipaddr_reset_filter(int oneline) +{ + memset(&filter, 0, sizeof(filter)); + filter.oneline = oneline; +} + +int default_scope(inet_prefix *lcl) +{ + if (lcl->family == AF_INET) { + if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) + return RT_SCOPE_HOST; + } + return 0; +} + +int ipaddr_modify(int cmd, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifaddrmsg ifa; + char buf[256]; + } req; + char *d = NULL; + char *l = NULL; + inet_prefix lcl; + inet_prefix peer; + int local_len = 0; + int peer_len = 0; + int brd_len = 0; + int any_len = 0; + int scoped = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "peer") == 0 || + strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + + if (peer_len) + duparg("peer", *argv); + get_prefix(&peer, *argv, req.ifa.ifa_family); + peer_len = peer.bytelen; + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = peer.family; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); + req.ifa.ifa_prefixlen = peer.bitlen; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (brd_len) + duparg("broadcast", *argv); + if (strcmp(*argv, "+") == 0) + brd_len = -1; + else if (strcmp(*argv, "-") == 0) + brd_len = -2; + else { + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); + brd_len = addr.bytelen; + } + } else if (strcmp(*argv, "anycast") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (any_len) + duparg("anycast", *argv); + get_addr(&addr, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = addr.family; + addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); + any_len = addr.bytelen; + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg(*argv, "invalid scope value."); + req.ifa.ifa_scope = scope; + scoped = 1; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else if (strcmp(*argv, "label") == 0) { + NEXT_ARG(); + l = *argv; + addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); + } else { + if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (local_len) + duparg2("local", *argv); + get_prefix(&lcl, *argv, req.ifa.ifa_family); + if (req.ifa.ifa_family == AF_UNSPEC) + req.ifa.ifa_family = lcl.family; + addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); + local_len = lcl.bytelen; + } + argc--; argv++; + } + if (d == NULL) { + fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); + return -1; + } + if (l && matches(d, l) != 0) { + fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l); + exit(1); + } + + if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { + peer = lcl; + addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); + } + if (req.ifa.ifa_prefixlen == 0) + req.ifa.ifa_prefixlen = lcl.bitlen; + + if (brd_len < 0 && cmd != RTM_DELADDR) { + inet_prefix brd; + int i; + if (req.ifa.ifa_family != AF_INET) { + fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n"); + return -1; + } + brd = peer; + if (brd.bitlen <= 30) { + for (i=31; i>=brd.bitlen; i--) { + if (brd_len == -1) + brd.data[0] |= htonl(1<<(31-i)); + else + brd.data[0] &= ~htonl(1<<(31-i)); + } + addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); + brd_len = brd.bytelen; + } + } + if (!scoped && cmd != RTM_DELADDR) + req.ifa.ifa_scope = default_scope(&lcl); + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + +int do_ipaddr(int argc, char **argv) +{ + if (argc < 1) + return ipaddr_list_or_flush(0, NULL, 0); + if (matches(*argv, "add") == 0) + return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipaddr_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return ipaddr_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return ipaddr_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv); + exit(-1); +} + diff --git a/ip/ipaddress.o b/ip/ipaddress.o new file mode 100644 index 0000000000000000000000000000000000000000..9d3fa3b6c0faf393f3b900fc2032722687e207a0 GIT binary patch literal 28616 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 literal 0 HcmV?d00001 diff --git a/ip/iplink.c b/ip/iplink.c new file mode 100644 index 0000000..520280e --- /dev/null +++ b/ip/iplink.c @@ -0,0 +1,429 @@ +/* + * iplink.c "ip link". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> +#include <linux/sockios.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + + +static void usage(void) __attribute__((noreturn)); + +void iplink_usage(void) +{ + fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n"); + fprintf(stderr, " arp { on | off } |\n"); + fprintf(stderr, " dynamic { on | off } |\n"); + fprintf(stderr, " multicast { on | off } |\n"); + fprintf(stderr, " allmulticast { on | off } |\n"); + fprintf(stderr, " promisc { on | off } |\n"); + fprintf(stderr, " trailers { on | off } |\n"); + fprintf(stderr, " txqueuelen PACKETS |\n"); + fprintf(stderr, " name NEWNAME |\n"); + fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); + fprintf(stderr, " mtu MTU }\n"); + fprintf(stderr, " ip link show [ DEVICE ]\n"); + exit(-1); +} + +static void usage(void) +{ + iplink_usage(); +} + +static int on_off(char *msg) +{ + fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); + return -1; +} + +static int get_ctl_fd(void) +{ + int s_errno; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + s_errno = errno; + fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + fd = socket(PF_INET6, SOCK_DGRAM, 0); + if (fd >= 0) + return fd; + errno = s_errno; + perror("Cannot create control socket"); + return -1; +} + +static int do_chflags(const char *dev, __u32 flags, __u32 mask) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCGIFFLAGS, &ifr); + if (err) { + perror("SIOCGIFFLAGS"); + close(fd); + return -1; + } + if ((ifr.ifr_flags^flags)&mask) { + ifr.ifr_flags &= ~mask; + ifr.ifr_flags |= mask&flags; + err = ioctl(fd, SIOCSIFFLAGS, &ifr); + if (err) + perror("SIOCSIFFLAGS"); + } + close(fd); + return err; +} + +static int do_changename(const char *dev, const char *newdev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); + fd = get_ctl_fd(); + if (fd < 0) + return -1; + err = ioctl(fd, SIOCSIFNAME, &ifr); + if (err) { + perror("SIOCSIFNAME"); + close(fd); + return -1; + } + close(fd); + return err; +} + +static int set_qlen(const char *dev, int qlen) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_qlen = qlen; + if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { + perror("SIOCSIFXQLEN"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int set_mtu(const char *dev, int mtu) +{ + struct ifreq ifr; + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + ifr.ifr_mtu = mtu; + if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { + perror("SIOCSIFMTU"); + close(s); + return -1; + } + close(s); + + return 0; +} + +static int get_address(const char *dev, int *htype) +{ + struct ifreq ifr; + struct sockaddr_ll me; + int alen; + int s; + + s = socket(PF_PACKET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_PACKET)"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + close(s); + return -1; + } + + memset(&me, 0, sizeof(me)); + me.sll_family = AF_PACKET; + me.sll_ifindex = ifr.ifr_ifindex; + me.sll_protocol = htons(ETH_P_LOOP); + if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { + perror("bind"); + close(s); + return -1; + } + + alen = sizeof(me); + if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { + perror("getsockname"); + close(s); + return -1; + } + close(s); + *htype = me.sll_hatype; + return me.sll_halen; +} + +static int parse_address(const char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) +{ + int alen; + + memset(ifr, 0, sizeof(*ifr)); + strncpy(ifr->ifr_name, dev, IFNAMSIZ); + ifr->ifr_hwaddr.sa_family = hatype; + alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); + if (alen < 0) + return -1; + if (alen != halen) { + fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); + return -1; + } + return 0; +} + +static int set_address(struct ifreq *ifr, int brd) +{ + int s; + + s = get_ctl_fd(); + if (s < 0) + return -1; + if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { + perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int do_set(int argc, char **argv) +{ + char *dev = NULL; + __u32 mask = 0; + __u32 flags = 0; + int qlen = -1; + int mtu = -1; + char *newaddr = NULL; + char *newbrd = NULL; + struct ifreq ifr0, ifr1; + char *newname = NULL; + int htype, halen; + + while (argc > 0) { + if (strcmp(*argv, "up") == 0) { + mask |= IFF_UP; + flags |= IFF_UP; + } else if (strcmp(*argv, "down") == 0) { + mask |= IFF_UP; + flags &= ~IFF_UP; + } else if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + newname = *argv; + } else if (matches(*argv, "address") == 0) { + NEXT_ARG(); + newaddr = *argv; + } else if (matches(*argv, "broadcast") == 0 || + strcmp(*argv, "brd") == 0) { + NEXT_ARG(); + newbrd = *argv; + } else if (matches(*argv, "txqueuelen") == 0 || + strcmp(*argv, "qlen") == 0 || + matches(*argv, "txqlen") == 0) { + NEXT_ARG(); + if (qlen != -1) + duparg("txqueuelen", *argv); + if (get_integer(&qlen, *argv, 0)) + invarg("Invalid \"txqueuelen\" value\n", *argv); + } else if (strcmp(*argv, "mtu") == 0) { + NEXT_ARG(); + if (mtu != -1) + duparg("mtu", *argv); + if (get_integer(&mtu, *argv, 0)) + invarg("Invalid \"mtu\" value\n", *argv); + } else if (strcmp(*argv, "multicast") == 0) { + NEXT_ARG(); + mask |= IFF_MULTICAST; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_MULTICAST; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_MULTICAST; + } else + return on_off("multicast"); + } else if (strcmp(*argv, "allmulticast") == 0) { + NEXT_ARG(); + mask |= IFF_ALLMULTI; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_ALLMULTI; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_ALLMULTI; + } else + return on_off("allmulticast"); + } else if (strcmp(*argv, "promisc") == 0) { + NEXT_ARG(); + mask |= IFF_PROMISC; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_PROMISC; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_PROMISC; + } else + return on_off("promisc"); + } else if (strcmp(*argv, "trailers") == 0) { + NEXT_ARG(); + mask |= IFF_NOTRAILERS; + if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOTRAILERS; + } else if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOTRAILERS; + } else + return on_off("trailers"); + } else if (strcmp(*argv, "arp") == 0) { + NEXT_ARG(); + mask |= IFF_NOARP; + if (strcmp(*argv, "on") == 0) { + flags &= ~IFF_NOARP; + } else if (strcmp(*argv, "off") == 0) { + flags |= IFF_NOARP; + } else + return on_off("noarp"); +#ifdef IFF_DYNAMIC + } else if (matches(*argv, "dynamic") == 0) { + NEXT_ARG(); + mask |= IFF_DYNAMIC; + if (strcmp(*argv, "on") == 0) { + flags |= IFF_DYNAMIC; + } else if (strcmp(*argv, "off") == 0) { + flags &= ~IFF_DYNAMIC; + } else + return on_off("dynamic"); +#endif + } else { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (dev) + duparg2("dev", *argv); + dev = *argv; + } + argc--; argv++; + } + + if (!dev) { + fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); + exit(-1); + } + + if (newaddr || newbrd) { + halen = get_address(dev, &htype); + if (halen < 0) + return -1; + if (newaddr) { + if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) + return -1; + } + if (newbrd) { + if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) + return -1; + } + } + + if (newname && strcmp(dev, newname)) { + if (do_changename(dev, newname) < 0) + return -1; + dev = newname; + } + if (qlen != -1) { + if (set_qlen(dev, qlen) < 0) + return -1; + } + if (mtu != -1) { + if (set_mtu(dev, mtu) < 0) + return -1; + } + if (newaddr || newbrd) { + if (newbrd) { + if (set_address(&ifr1, 1) < 0) + return -1; + } + if (newaddr) { + if (set_address(&ifr0, 0) < 0) + return -1; + } + } + if (mask) + return do_chflags(dev, flags, mask); + return 0; +} + +int do_iplink(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "set") == 0) + return do_set(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return ipaddr_list_link(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return ipaddr_list_link(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); + exit(-1); +} diff --git a/ip/iplink.o b/ip/iplink.o new file mode 100644 index 0000000000000000000000000000000000000000..ac0829c3c3889203e413430e032bf345af2c4ea3 GIT binary patch literal 11376 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 literal 0 HcmV?d00001 diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c new file mode 100644 index 0000000..1cdab0b --- /dev/null +++ b/ip/ipmaddr.c @@ -0,0 +1,343 @@ +/* + * ipmaddr.c "ip maddress". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + +static struct { + char *dev; + int family; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip maddr [ add | del ] MULTIADDR dev STRING\n"); + fprintf(stderr, " ip maddr show [ dev STRING ]\n"); + exit(-1); +} + +static int parse_hex(char *str, unsigned char *addr) +{ + int len=0; + + while (*str) { + int tmp; + if (str[1] == 0) + return -1; + if (sscanf(str, "%02x", &tmp) != 1) + return -1; + addr[len] = tmp; + len++; + str += 2; + } + return len; +} + +struct ma_info +{ + struct ma_info *next; + int index; + int users; + char *features; + char name[IFNAMSIZ]; + inet_prefix addr; +}; + +void maddr_ins(struct ma_info **lst, struct ma_info *m) +{ + struct ma_info *mp; + + for (; (mp=*lst) != NULL; lst = &mp->next) { + if (mp->index > m->index) + break; + } + m->next = *lst; + *lst = m; +} + +void read_dev_mcast(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/dev_mcast", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m; + int len; + int st; + + memset(&m, 0, sizeof(m)); + sscanf(buf, "%d%s%d%d%s", &m.index, m.name, &m.users, &st, + hexa); + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + m.addr.family = AF_PACKET; + + len = parse_hex(hexa, (unsigned char*)&m.addr.data); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + if (st) + ma->features = "static"; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +void read_igmp(struct ma_info **result_p) +{ + struct ma_info m; + char buf[256]; + FILE *fp = fopen("/proc/net/igmp", "r"); + + if (!fp) + return; + memset(&m, 0, sizeof(m)); + fgets(buf, sizeof(buf), fp); + + 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)); + + if (buf[0] != '\t') { + sscanf(buf, "%d%s", &m.index, m.name); + continue; + } + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + sscanf(buf, "%08x%d", (__u32*)&m.addr.data, &m.users); + + ma = malloc(sizeof(m)); + memcpy(ma, &m, sizeof(m)); + maddr_ins(result_p, ma); + } + fclose(fp); +} + + +void read_igmp6(struct ma_info **result_p) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/igmp6", "r"); + + if (!fp) + return; + + while (fgets(buf, sizeof(buf), fp)) { + char hexa[256]; + struct ma_info m; + int len; + + memset(&m, 0, sizeof(m)); + sscanf(buf, "%d%s%s%d", &m.index, m.name, hexa, &m.users); + + if (filter.dev && strcmp(filter.dev, m.name)) + continue; + + m.addr.family = AF_INET6; + + len = parse_hex(hexa, (unsigned char*)&m.addr.data); + if (len >= 0) { + struct ma_info *ma = malloc(sizeof(m)); + + memcpy(ma, &m, sizeof(m)); + + ma->addr.bytelen = len; + ma->addr.bitlen = len<<3; + maddr_ins(result_p, ma); + } + } + fclose(fp); +} + +static void print_maddr(FILE *fp, struct ma_info *list) +{ + fprintf(fp, "\t"); + + if (list->addr.family == AF_PACKET) { + SPRINT_BUF(b1); + fprintf(fp, "link %s", ll_addr_n2a((unsigned char*)list->addr.data, + list->addr.bytelen, 0, + b1, sizeof(b1))); + } else { + char abuf[256]; + switch(list->addr.family) { + case AF_INET: + fprintf(fp, "inet "); + break; + case AF_INET6: + fprintf(fp, "inet6 "); + break; + default: + fprintf(fp, "family %d ", list->addr.family); + break; + } + fprintf(fp, "%s", + format_host(list->addr.family, + -1, + list->addr.data, + abuf, sizeof(abuf))); + } + if (list->users != 1) + fprintf(fp, " users %d", list->users); + if (list->features) + fprintf(fp, " %s", list->features); + fprintf(fp, "\n"); +} + +static void print_mlist(FILE *fp, struct ma_info *list) +{ + int cur_index = 0; + + for (; list; list = list->next) { + if (oneline) { + cur_index = list->index; + fprintf(fp, "%d:\t%s%s", cur_index, list->name, _SL_); + } else if (cur_index != list->index) { + cur_index = list->index; + fprintf(fp, "%d:\t%s\n", cur_index, list->name); + } + print_maddr(fp, list); + } +} + +static int multiaddr_list(int argc, char **argv) +{ + struct ma_info *list = NULL; + + if (!filter.family) + filter.family = preferred_family; + + while (argc > 0) { + if (1) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (filter.dev) + duparg2("dev", *argv); + filter.dev = *argv; + } + argv++; argc--; + } + + if (!filter.family || filter.family == AF_PACKET) + read_dev_mcast(&list); + if (!filter.family || filter.family == AF_INET) + read_igmp(&list); + if (!filter.family || filter.family == AF_INET6) + read_igmp6(&list); + print_mlist(stdout, list); + return 0; +} + +int multiaddr_modify(int cmd, int argc, char **argv) +{ + struct ifreq ifr; + int fd; + + memset(&ifr, 0, sizeof(ifr)); + + if (cmd == RTM_NEWADDR) + cmd = SIOCADDMULTI; + else + cmd = SIOCDELMULTI; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (ifr.ifr_name[0]) + duparg("dev", *argv); + strncpy(ifr.ifr_name, *argv, IFNAMSIZ); + } else { + if (matches(*argv, "address") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (ifr.ifr_hwaddr.sa_data[0]) + duparg("address", *argv); + if (ll_addr_a2n(ifr.ifr_hwaddr.sa_data, 14, *argv) < 0) { + fprintf(stderr, "Error: \"%s\" is not a legal ll address.\n", *argv); + exit(1); + } + } + argc--; argv++; + } + if (ifr.ifr_name[0] == 0) { + fprintf(stderr, "Not enough information: \"dev\" is required.\n"); + exit(-1); + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot create socket"); + exit(1); + } + if (ioctl(fd, cmd, (char*)&ifr) != 0) { + perror("ioctl"); + exit(1); + } + close(fd); + + exit(0); +} + + +int do_multiaddr(int argc, char **argv) +{ + if (argc < 1) + return multiaddr_list(0, NULL); + if (matches(*argv, "add") == 0) + return multiaddr_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return multiaddr_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return multiaddr_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip maddr help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmaddr.o b/ip/ipmaddr.o new file mode 100644 index 0000000000000000000000000000000000000000..fc862e5f5fdc7cd2bcdfbda5fbba8910cd65b0c9 GIT binary patch literal 9832 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 N@_qdY8!mr3`Y$FelZ^lX literal 0 HcmV?d00001 diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c new file mode 100644 index 0000000..cdaeb6f --- /dev/null +++ b/ip/ipmonitor.c @@ -0,0 +1,165 @@ +/* + * ipmonitor.c "ip monitor". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 <time.h> + +#include "utils.h" +#include "ip_common.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip monitor [ all | LISTofOBJECTS ]\n"); + exit(-1); +} + + +int accept_msg(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + + if (n->nlmsg_type == RTM_NEWROUTE || n->nlmsg_type == RTM_DELROUTE) { + print_route(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWLINK || n->nlmsg_type == RTM_DELLINK) { + ll_remember_index(who, n, NULL); + print_linkinfo(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) { + print_addrinfo(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWNEIGH || n->nlmsg_type == RTM_DELNEIGH) { + print_neigh(who, n, arg); + return 0; + } + if (n->nlmsg_type == RTM_NEWPREFIX) { + print_prefix(who, n, arg); + return 0; + } + if (n->nlmsg_type == 15) { + char *tstr; + time_t secs = ((__u32*)NLMSG_DATA(n))[0]; + long usecs = ((__u32*)NLMSG_DATA(n))[1]; + tstr = asctime(localtime(&secs)); + tstr[strlen(tstr)-1] = 0; + fprintf(fp, "Timestamp: %s %lu us\n", tstr, usecs); + return 0; + } + if (n->nlmsg_type == RTM_NEWQDISC || + n->nlmsg_type == RTM_DELQDISC || + n->nlmsg_type == RTM_NEWTCLASS || + n->nlmsg_type == RTM_DELTCLASS || + n->nlmsg_type == RTM_NEWTFILTER || + n->nlmsg_type == RTM_DELTFILTER) + return 0; + if (n->nlmsg_type != NLMSG_ERROR && n->nlmsg_type != NLMSG_NOOP && + n->nlmsg_type != NLMSG_DONE) { + fprintf(fp, "Unknown message: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + } + return 0; +} + +int do_ipmonitor(int argc, char **argv) +{ + struct rtnl_handle rth; + char *file = NULL; + unsigned groups = ~RTMGRP_TC; + int llink=0; + int laddr=0; + int lroute=0; + int lprefix=0; + + ipaddr_reset_filter(1); + iproute_reset_filter(); + ipneigh_reset_filter(); + + while (argc > 0) { + if (matches(*argv, "file") == 0) { + NEXT_ARG(); + file = *argv; + } else if (matches(*argv, "link") == 0) { + llink=1; + groups = 0; + } else if (matches(*argv, "address") == 0) { + laddr=1; + groups = 0; + } else if (matches(*argv, "route") == 0) { + lroute=1; + groups = 0; + } else if (matches(*argv, "prefix") == 0) { + lprefix=1; + groups = 0; + } else if (strcmp(*argv, "all") == 0) { + groups = ~RTMGRP_TC; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"ip monitor help\".\n", *argv); + exit(-1); + } + argc--; argv++; + } + + if (llink) + groups |= RTMGRP_LINK; + if (laddr) { + if (!preferred_family || preferred_family == AF_INET) + groups |= RTMGRP_IPV4_IFADDR; + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_IFADDR; + } + if (lroute) { + if (!preferred_family || preferred_family == AF_INET) + groups |= RTMGRP_IPV4_ROUTE; + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_ROUTE; + } + if (lprefix) { + if (!preferred_family || preferred_family == AF_INET6) + groups |= RTMGRP_IPV6_PREFIX; + } + + if (file) { + FILE *fp; + fp = fopen(file, "r"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + return rtnl_from_file(fp, accept_msg, (void*)stdout); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + ll_init_map(&rth); + + if (rtnl_listen(&rth, accept_msg, (void*)stdout) < 0) + exit(2); + + exit(0); +} diff --git a/ip/ipmonitor.o b/ip/ipmonitor.o new file mode 100644 index 0000000000000000000000000000000000000000..9783beffb35fd568f1727f4a20af934326a0a71e GIT binary patch literal 5208 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 literal 0 HcmV?d00001 diff --git a/ip/ipmroute.c b/ip/ipmroute.c new file mode 100644 index 0000000..b24caee --- /dev/null +++ b/ip/ipmroute.c @@ -0,0 +1,205 @@ +/* + * ipmroute.c "ip mroute". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "utils.h" + +char filter_dev[16]; +int filter_family; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip mroute show [ PREFIX ] [ from PREFIX ] [ iif DEVICE ]\n"); +#if 0 + fprintf(stderr, "Usage: ip mroute [ add | del ] DESTINATION from SOURCE [ iif DEVICE ] [ oif DEVICE ]\n"); +#endif + exit(-1); +} + +char *viftable[32]; + +struct rtfilter +{ + inet_prefix mdst; + inet_prefix msrc; +} filter; + +void read_viftable(void) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/ip_mr_vif", "r"); + + if (!fp) + return; + + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp)) { + int vifi; + char dev[256]; + + if (sscanf(buf, "%d%s", &vifi, dev) < 2) + continue; + + if (vifi<0 || vifi>31) + continue; + + viftable[vifi] = strdup(dev); + } + fclose(fp); +} + +void read_mroute_list(FILE *ofp) +{ + char buf[256]; + FILE *fp = fopen("/proc/net/ip_mr_cache", "r"); + + if (!fp) + return; + + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp)) { + inet_prefix maddr, msrc; + unsigned pkts, b, w; + int vifi; + char oiflist[256]; + char sbuf[256]; + char mbuf[256]; + char obuf[256]; + + oiflist[0] = 0; + if (sscanf(buf, "%x%x%d%u%u%u%s", maddr.data, msrc.data, &vifi, + &pkts, &b, &w, oiflist) < 6) + continue; + + if (vifi!=-1 && (vifi < 0 || vifi>31)) + continue; + + if (filter_dev[0] && (vifi<0 || strcmp(filter_dev, viftable[vifi]))) + continue; + if (filter.mdst.family && inet_addr_match(&maddr, &filter.mdst, filter.mdst.bitlen)) + continue; + if (filter.msrc.family && inet_addr_match(&msrc, &filter.msrc, filter.msrc.bitlen)) + continue; + + snprintf(obuf, sizeof(obuf), "(%s, %s)", + format_host(AF_INET, 4, &msrc.data[0], sbuf, sizeof(sbuf)), + format_host(AF_INET, 4, &maddr.data[0], mbuf, sizeof(mbuf))); + + fprintf(ofp, "%-32s Iif: ", obuf); + + if (vifi == -1) + fprintf(ofp, "unresolved "); + else + fprintf(ofp, "%-10s ", viftable[vifi]); + + if (oiflist[0]) { + char *next = NULL; + char *p = oiflist; + int ovifi, ottl; + + fprintf(ofp, "Oifs: "); + + while (p) { + next = strchr(p, ' '); + if (next) { + *next = 0; + next++; + } + if (sscanf(p, "%d:%d", &ovifi, &ottl)<2) { + p = next; + continue; + } + p = next; + + fprintf(ofp, "%s", viftable[ovifi]); + if (ottl>1) + fprintf(ofp, "(ttl %d) ", ovifi); + else + fprintf(ofp, " "); + } + } + + if (show_stats && b) { + fprintf(ofp, "%s %u packets, %u bytes", _SL_, pkts, b); + if (w) + fprintf(ofp, ", %u arrived on wrong iif.", w); + } + fprintf(ofp, "\n"); + } + fclose(fp); +} + + +static int mroute_list(int argc, char **argv) +{ + while (argc > 0) { + if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + strncpy(filter_dev, *argv, sizeof(filter_dev)-1); + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, AF_INET); + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&filter.mdst, *argv, AF_INET); + } + argv++; argc--; + } + + read_viftable(); + read_mroute_list(stdout); + return 0; +} + +int do_multiroute(int argc, char **argv) +{ + if (argc < 1) + return mroute_list(0, NULL); +#if 0 + if (matches(*argv, "add") == 0) + return mroute_modify(RTM_NEWADDR, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return mroute_modify(RTM_DELADDR, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return mroute_get(argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return mroute_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mroute help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmroute.o b/ip/ipmroute.o new file mode 100644 index 0000000000000000000000000000000000000000..3783d575509c50f9245579add946c27191838c6b GIT binary patch literal 6872 zcmbVQeT);w6`%V6u6)=RLg+zhv%T1fkq&bPlpsP{8xGiO9D?AER084{dlzr<S9jOk zp%OmiGh}5<u76ail(cH9D3Mw!(Jz81<y`KPLu!%QCanl9YLu#iYl3q5C_ObH?t8N{ z=V9^w(Z0hwGr#%0H*em&nVsd(hUmuTrY6FriF}3BTO1`s{#?BtrqwX<kU2yyzSky~ z=OHqJkF#NToJR}S;YUVlAhf2c>M;C^^q;3VJ|cbn+vFqfbu=@wT&l{}Yu=%w&=;+| z<ZA~r(MsCaE?XDQqgPSu8nq=`|Gd3js+v)`(&w8Ssz$8|Is)g`E*|`SxyLuRICby9 zc+`4dPF!}{kge9M=#Pz;s)b+3*86tj$QXLJV^?HnWY;K8A@T&Z9@*Y&eMTo7>1We; zW=7pYuQlc!dW*V$(s#HOr*X~~hO#~rdw}7n^$wc~8?jXN4m}5&a%IRDZo%>e-&3I3 zTgkOXD^cI7Xr;&3O`VXf9*7Mz*UH7wR@s`6tt&V~fZGsW{km-eMs4+ls&q!hsTl{$ z<vw3`WnEKoO7b527A(-=M{xSY^q2nVedZeg$cYPda&mdqYuH0o!L!n1b+)&(4BTX@ z-qJ0g$(7^2F#2v?iJZX&I}eVN5r`m*E%rmp?!;A`?w74mJK~iCzOZclrq{ZB7Ssq0 z4)YyXI2@|7<^4D1!_syar)u7zmvP+h@;<q|fI7}J4@2}Jmc(27D?H1vQiMRx%mwY$ zI4uj>QS0W&Y$&POM$1ytKC9JuTFFPoaKY`&h&&mIje1A!L9_Ds>sUlBJMGbW2k1?_ z4G&I%WWXh*&Q6E+!&|`m39#fcHB^SRj#&0=F|FBr`+RF*m4-IMLh#A5NlvW6m8+bj zQ;k|5oSg}N(5*<<k`DCl5?K3yLs2gM#XEEjMm{fr#&quEZX7jd!gS=?s0^vsg$odX zezpd(b(I>0Ep|>$^!Nf0Lb<%Qo-xG(zOJT26^txewmtYZx;6)z4%a5001t-yty|k< z>mxi?N;eK}8J5d;drvnPuWN;c-qVn>P?-l6K-5AjR9axWK3cm38vyv;6IHM)msg(k zLTP6(bkr`<kxMrY{ghVZGR=3pUL0!+Rd?8%8}_$cepp<V!u{O#!4%rXRF=ob;mI<Z znt%5t?xtDr*ly+uGtkXpaBuds3BSX-xro~oR_G-4%RVmE;p@-;r&i+)^v~Mqo0P4z zZwf-xQL0(DAX*1ChvC9vxni_co`zi731>kW&(Pvn$Hez(dfhfd^G^<4bIbu0I<GIJ zPT=`nHyFAZs!^l8)F=%pgn>qnW#kAXq`eJDzY*V~tdX?5l+kkqQ<02RZa~^0ZP~hE zWAE3cT~Jl^Tt;kbnkw~d*xtK-gR~1bCYANMOeUU9N}YbAQ_>8nklmZj4P?6{Q{OLj z*3GAsbiOk<o9JYDUe6_#XBBg~mXBrh*gj1q{-ocyqazVdq!i*GgukS}K>rL9@Ecu{ z-&jig%T}&1q+U&3BSAgXZ9rj2lD{D3<B7eBiL`>Wd%vj|gjVCauI*EjQZ6eE=(+43 z3FZ?dk87#{76PW3mi)=3U?jQ5pCpB>t{AyA@(IR)p#LPT879F%ku+4OEU*hWT$={l z=gw*yhGP$93H#9oJeIObkEfm1y5PB%VOX4F^>v>1sHY?1ks_YJCQr4sIOAyR@s>(6 za?^~1MGTKm5a4g}bew2;w)txwX`Lq!@pMN#t3h*4an{k9$7fXVL<jM~?<B3+k9(M( z5l{Qk=JlSAV#_9v1YPL&n>K|$UuE^p9_eUv5eD6~e*GFLuxWGuQfXyyRd9v0qPu%l zs5|tK6xa&UlH;b0FMBYkCRW0<gU0@h8Q%@1soQ0W*K{Qv51PuLNrK6^87INrhCzaQ z4jV`h1y|qR3_%lhlvGUB;~9knvpG`<f;Y<y6PBF>6OfNemMxAjkN>qQfdYmI&ume` ztnq-O!(P=o^9xXAp|68bo4m_z=G*LYE)+NpYmyEdCigR(Ei*$2Njb*l9nP^ZztHCY zs~-Yg^lJtDMB(EPiXhav|17OY7_dhAZ!`aY>(J+VyzFE8c)*KtYL?xa6hWx-@j3iB zRGcxvzNZ%34DLV1A5C(79{(o+<G$D$_VWjvD1r^R*#;P@aD9xgD9*7kO`L0o{6Dbq z2SjD4JN2>cj2)_6#0i5;GUNEAfDoS+pFe(HG#dX5OY{dF13H?Z-B^&<N<OGH>TS7W z7B;J;p(p5HqZLM+UMO!H0_WH)*yrall3MQA<%G;<{4Hz<eqP#qmW0?4q=jJA?vxfX z{koj){1VihX9jlqUGOd!9P?;C%&Qwp6#gFhD%2a<-{68j>Vj`|!FK?D7x}OzK}m%r zKoS~_#@!1z`Z;N{r{7-(ppAC84lj?n*g3)Y5|k(byCC0zlIt>v7B9NsKXSo;=7Rs) z1s`?6-*CY%yWoFw!T;fc-*CY{cELY&!Dqp`HHzC}z;V6uY%B45Kmc%DFTV45NwmCu zkqjnC0q>T?ZpQ38lbKX>ou~u4W-3G()J&o)@g&~;;7X8Ih?>hQS)%TNdmAx~L_DjK zSPX6e$wHpgNr`mMP;5=nx~ip3MUN$weFRhznLM!(u&QU`W-OJ%Yg;z2YgtnzDl73G z2SdbRL^WS96GVeMX)K;h>M>ACq)2RAG)4`kz;j@LXQrM_<o83FggY~#G`L=Z*Ri~= zsM;XdfD1@It(Zy-Zz6CrCdphZQ%IW{y=F7dVro32rT4>{@RJ13YCNMp#Va}p)=}`} zU`s&Tc&|V~KAzp2??(^>aS!}Bz7jzYw8O{3_G)~9aGbv@WE-Y-FfX{BZwffRn>c>N z1;_n}!tF1#o8)_}#PRTq<#v84;CBo7MTX-C>H=2B;u5Q)9nqgF0*;^U-2O)l=l-xJ zx#hrle7%t8C}>}d!@~kDjvIBs2N}-C4N<Fb7Z><q9L5Fw^T7SYPKVkR!4BeF-eI^i zKGy_0_)h0>_?Lh$0-W1#W(j}=;`qUivQ)tF>cs6tUGSX@=kpDt5s;L?#~5=vri0&0 z*@PSr`1no2`NIx=KjS~oaAzET=wj!zfPYc2|Av6$JDQLCcL5jI{bK<a^KBlxJYhG+ z6W17J0mIQx#Cc9H5%~84&d2Q*aOA_cJ(Wioj&Vku%Q}HC+SxAP5|Fw5-GUuFA9gZB zX@M`s|7i!m!1%{p_%A#7!;Jr9fiIq8rycy~7{4m;MSuP*;9mwVpVySb&Wp?rzcS*w zi2H5<dyzTwXA#4Z-znH%Cg5V8L>SKV{}ndwMu9KR_XUA3?(0_s{(Zu@rvx1LD4*}E z0`3#=a{`WMFz3I?aPH^Np%3LPfsgML&cDfUXWo7y@bR0K^H;E!7Po%}je|VIaEzyD zUl#acK0hY#Mf-gM{s7qG<J$k1Tz~K2y$xkp;QOJ?`KKAq{k%Zgg#5~dKknd<Gyd;f z_?I1g9*3(g{MqdL7~><}2j(*z{X|v%o?ayI#eH#~fQ$D533O5LHGpGq39|~vyIDK$ Tz$aPzlmqAANtYcszd!yLKki{* literal 0 HcmV?d00001 diff --git a/ip/ipneigh.c b/ip/ipneigh.c new file mode 100644 index 0000000..e8ab291 --- /dev/null +++ b/ip/ipneigh.c @@ -0,0 +1,475 @@ +/* + * ipneigh.c "ip neigh". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <net/if.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY) + +static struct +{ + int family; + int index; + int state; + int unused_only; + inet_prefix pfx; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n" + " [ nud { permanent | noarp | stale | reachable } ]\n" + " | proxy ADDR } [ dev DEV ]\n"); + fprintf(stderr, " ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"); + exit(-1); +} + +int nud_state_a2n(unsigned *state, char *arg) +{ + if (matches(arg, "permanent") == 0) + *state = NUD_PERMANENT; + else if (matches(arg, "reachable") == 0) + *state = NUD_REACHABLE; + else if (strcmp(arg, "noarp") == 0) + *state = NUD_NOARP; + else if (strcmp(arg, "none") == 0) + *state = NUD_NONE; + else if (strcmp(arg, "stale") == 0) + *state = NUD_STALE; + else if (strcmp(arg, "incomplete") == 0) + *state = NUD_INCOMPLETE; + else if (strcmp(arg, "delay") == 0) + *state = NUD_DELAY; + else if (strcmp(arg, "probe") == 0) + *state = NUD_PROBE; + else if (matches(arg, "failed") == 0) + *state = NUD_FAILED; + else { + if (get_unsigned(state, arg, 0)) + return -1; + if (*state>=0x100 || (*state&((*state)-1))) + return -1; + } + return 0; +} + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + + +static int ipneigh_modify(int cmd, int flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + char *d = NULL; + int dst_ok = 0; + int lladdr_ok = 0; + char * lla = NULL; + inet_prefix dst; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.ndm.ndm_family = preferred_family; + req.ndm.ndm_state = NUD_PERMANENT; + + while (argc > 0) { + if (matches(*argv, "lladdr") == 0) { + NEXT_ARG(); + if (lladdr_ok) + duparg("lladdr", *argv); + lla = *argv; + lladdr_ok = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned state; + NEXT_ARG(); + if (nud_state_a2n(&state, *argv)) + invarg("nud state is bad", *argv); + req.ndm.ndm_state = state; + } else if (matches(*argv, "proxy") == 0) { + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg("address", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + req.ndm.ndm_flags |= NTF_PROXY; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + d = *argv; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) { + NEXT_ARG(); + } + if (dst_ok) + duparg2("to", *argv); + get_addr(&dst, *argv, preferred_family); + dst_ok = 1; + } + argc--; argv++; + } + if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) { + fprintf(stderr, "Device and destination are required arguments.\n"); + exit(-1); + } + req.ndm.ndm_family = dst.family; + addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen); + + if (lla && strcmp(lla, "null")) { + __u8 llabuf[16]; + int l; + + l = ll_addr_a2n(llabuf, sizeof(llabuf), lla); + addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + exit(0); +} + + +int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX+1]; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { + fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + + return 0; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + if (filter.family && filter.family != r->ndm_family) + return 0; + if (filter.index && filter.index != r->ndm_ifindex) + return 0; + if (!(filter.state&r->ndm_state) && + (r->ndm_state || !(filter.state&0x100)) && + (r->ndm_family != AF_DECnet)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[NDA_DST]) { + if (filter.pfx.family) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = r->ndm_family; + memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); + if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) + return 0; + } + } + if (filter.unused_only && tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (ci->ndm_refcnt) + return 0; + } + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELNEIGH; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (tb[NDA_DST]) { + fprintf(fp, "%s ", + format_host(r->ndm_family, + RTA_PAYLOAD(tb[NDA_DST]), + RTA_DATA(tb[NDA_DST]), + abuf, sizeof(abuf))); + } + if (!filter.index && r->ndm_ifindex) + fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex)); + if (tb[NDA_LLADDR]) { + SPRINT_BUF(b1); + fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ll_index_to_type(r->ndm_ifindex), + b1, sizeof(b1))); + } + if (r->ndm_flags & NTF_ROUTER) { + fprintf(fp, " router"); + } + if (tb[NDA_CACHEINFO] && show_stats) { + static int hz; + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (!hz) + hz = get_hz(); + if (ci->ndm_refcnt) + printf(" ref %d", ci->ndm_refcnt); + fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz, + 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; + fprintf(fp, " "); + +#define PRINT_FLAG(f) if (nud & NUD_##f) { \ + nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); } + PRINT_FLAG(INCOMPLETE); + PRINT_FLAG(REACHABLE); + PRINT_FLAG(STALE); + PRINT_FLAG(DELAY); + PRINT_FLAG(PROBE); + PRINT_FLAG(FAILED); + PRINT_FLAG(NOARP); + PRINT_FLAG(PERMANENT); +#undef PRINT_FLAG + } + fprintf(fp, "\n"); + + fflush(fp); + return 0; +} + +void ipneigh_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.state = ~0; +} + +int do_show_or_flush(int argc, char **argv, int flush) +{ + char *filter_dev = NULL; + struct rtnl_handle rth; + int state_given = 0; + + ipneigh_reset_filter(); + + if (!filter.family) + filter.family = preferred_family; + + if (flush) { + if (argc <= 0) { + fprintf(stderr, "Flush requires arguments.\n"); + return -1; + } + filter.state = ~(NUD_PERMANENT|NUD_NOARP); + } else + filter.state = 0xFF & ~NUD_NOARP; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (filter_dev) + duparg("dev", *argv); + filter_dev = *argv; + } else if (strcmp(*argv, "unused") == 0) { + filter.unused_only = 1; + } else if (strcmp(*argv, "nud") == 0) { + unsigned state; + NEXT_ARG(); + if (!state_given) { + state_given = 1; + filter.state = 0; + } + if (nud_state_a2n(&state, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("nud state is bad", *argv); + state = ~0; + if (flush) + state &= ~NUD_NOARP; + } + if (state == 0) + state = 0x100; + filter.state |= state; + } else { + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&filter.pfx, *argv, filter.family); + if (filter.family == AF_UNSPEC) + filter.family = filter.pfx.family; + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (filter_dev) { + if ((filter.index = ll_name_to_index(filter_dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); + return -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + filter.state &= ~NUD_FAILED; + + for (;;) { + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +int do_ipneigh(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); + if (matches(*argv, "change") == 0 || + strcmp(*argv, "chg") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) { + fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n"); + return -1; + } + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) + return do_show_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show_or_flush(0, NULL, 0); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipneigh.o b/ip/ipneigh.o new file mode 100644 index 0000000000000000000000000000000000000000..876eab67f4030ab835a39f981be7bf94e481be7b GIT binary patch literal 15680 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 X`u$HBf-wqLzyB?edKCVkTtEK@Kk)4! literal 0 HcmV?d00001 diff --git a/ip/ipprefix.c b/ip/ipprefix.c new file mode 100644 index 0000000..61d12f9 --- /dev/null +++ b/ip/ipprefix.c @@ -0,0 +1,106 @@ +/* + * Copyright (C)2005 USAGI/WIDE Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/icmp6.h> +#include "utils.h" + +/* prefix flags; see kernel's net/ipv6/addrconf.c and include/net/if_inet6.h */ +#define IF_PREFIX_ONLINK 0x01 +#define IF_PREFIX_AUTOCONF 0x02 + +int print_prefix(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct prefixmsg *prefix = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + int family = preferred_family; + + if (n->nlmsg_type != RTM_NEWPREFIX) { + fprintf(stderr, "Not a prefix: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*prefix)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (family == AF_UNSPEC) + family = AF_INET6; + if (family != AF_INET6) + return 0; + + if (prefix->prefix_family != AF_INET6) { + fprintf(stderr, "wrong family %d\n", prefix->prefix_family); + return 0; + } + if (prefix->prefix_type != ND_OPT_PREFIX_INFORMATION) { + fprintf(stderr, "wrong ND type %d\n", prefix->prefix_type); + return 0; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(prefix), len); + + fprintf(fp, "prefix "); + + if (tb[PREFIX_ADDRESS]) { + struct in6_addr *pfx; + char abuf[256]; + + pfx = (struct in6_addr *)RTA_DATA(tb[PREFIX_ADDRESS]); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "%s", rt_addr_n2a(family, sizeof(*pfx), pfx, + abuf, sizeof(abuf))); + } + fprintf(fp, "/%u ", prefix->prefix_len); + + fprintf(fp, "dev %s ", ll_index_to_name(prefix->prefix_ifindex)); + + if (prefix->prefix_flags & IF_PREFIX_ONLINK) + fprintf(fp, "onlink "); + if (prefix->prefix_flags & IF_PREFIX_AUTOCONF) + fprintf(fp, "autoconf "); + + if (tb[PREFIX_CACHEINFO]) { + struct prefix_cacheinfo *pc; + pc = (struct prefix_cacheinfo *)tb[PREFIX_CACHEINFO]; + + fprintf(fp, "valid %u ", pc->valid_time); + fprintf(fp, "preferred %u ", pc->preferred_time); + } + + fprintf(fp, "\n"); + fflush(fp); + + return 0; +} + diff --git a/ip/ipprefix.o b/ip/ipprefix.o new file mode 100644 index 0000000000000000000000000000000000000000..5186e995c90b49aec7cdb1c2f44a9bcd042711bd GIT binary patch literal 3272 zcmbtWU1%It6u!x>O}hQrwXslXD1&Y!RXg2HgFm54V)HZV8bwVN5vSALnPkcC>@st= z-8^U;(v*&CppPmd3JQ5B+85JON*Y?h9}q<FNeWg$yF{7_B0<#koSA#G*W1jC9@shO ze&;*qo_p?{J9}&>Gu+zJLb$Y$M@e(UQ9^D;ns&c$_LD6nMl`RGyq<x_s^)#Gc`qlw z&v?1yO>O#@Hz7^@+;B8AXD0@0TWi}f%9>g2rB4n5zCo)!f=K+_=Y!sti)f~ot_Mo1 zy_2j$xK=xpJPtZj`yhGB*JqNa5ug4PvtO~S(*ra0&iJX5Fq-{_wxyS5{)knU;tRux z{V}>DzVNl5m`%m<FTXnwUl?lNAE7Z$SiY*w9qiQRE+%pDgWea5Tc8rZ8qKR;XoJBb zILIZeFMw+&lI;L#vrCmp$Tzkzn8{mVvkcT;jrvT6U)Gu~m_9b&5}*ADvdy=~XI}@6 zYfktHzpK2cc{eV_)~51S{6zI1|86ugbBCho-!<>&3s|X-51dC+^R8vQ9~Ki3C5GP6 zyw8H@7aEO5?YStFAvU|#A=SM+gDc($X`~g`UxEZ7-N1&%>S-hdyDDwzdGIvqT~PmM zi7JLtc1$Z@?Nho^4^^9w7&+>c>~Y01iuuBn(v{=D=%7NU%BGJ5p$ggCRZ&RJJfd{D z3OQmF@;L<&beoQ21|y?FOWBfbDWqf<^7f=c9vd6!Q-W#iLeU*p3a0Jng$1MB5h&<r zOEs~%tsRdWN)mpZz7;%Dt$}`GuJvT}`K|9o-iXZHhS11?1AR*O$mrN^WuMxs_9#86 zRBt+!eo*Ot7OK(=8sK{#P_68~-Gr#_RFN8oK~pEt6Wnslf}v8gN{N~?s6o`ju1i#> zgaf3f)iex|WKQUoV-!uI+9hhLF#R5v8reyrW=qAQY15$E|Nm4V1-4{^l&YVNd0)62 z&!Z?iVGtDoW}n3qhr;feO<>|Y?TCTw2o$*oG~&W_<a^3&KNxLI$#dNg9?bb)<I@m} z^Y_3{6h8l3A_z8eQMevrwp?z471WPpLHPPP)DMQdCiI=%jL&ADuz$EmJf8ROX~1|b zV6MqCazmI$=IVgU7*csW_D2+%dpkJSn`)YY6Jjvfay*XZ-eE`z?`V)!7J%)*BPFjN zvGwNfX6Nq&_(hTD7k#3QyeRp#A!6K)+_H+m6Ir(stZ%&?M%=$)c-l1`x#ex-`T6SC zsJ;>LJM(W0`?XD|lfTDz0xrM97<q39j{CnJ|KSjPI0Szx1b-z2&xPQVA-EHQzZQa% zd^z~?s9E^N<ZY@4!{!G^4?bJOr8)Qj5zA*;q-;2@sl&}qDS$_vJT*z7py%zJS=DJt zxAE4t$`zU=mQ|>@6QpPsU6T@r>P9Z-=ys3c`(3Dvko){+@DAWVgg^1i3gZ8lf-$}u z`0jID0~-ao;_1PA00sHD_niMef*_o~D;Sb_rsez}3%Ho)GQ)YEy=;6{;NyQRKJSXe zA7JC(1pZcmzlpt=Jpbd2x1Hhgx^@Y8T*z~`lxLLXNeO&$UAlndIp_6{3pn0!96u)D z+XVb=0q+p-4+R|mi|{<3Fr2UVAox%&3VgB7D-!>Z&nD!n5dJlZZ!`W+0$=R^UlPB{ m_^sG@5L6<+L%_xJxs&01UB@vP<Sv0Pu1oQ~!FPdCeB>X`@)nZ- literal 0 HcmV?d00001 diff --git a/ip/iproute.c b/ip/iproute.c new file mode 100644 index 0000000..1e23e49 --- /dev/null +++ b/ip/iproute.c @@ -0,0 +1,1416 @@ +/* + * iproute.c "ip route". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/in_route.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n"); + fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); + fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); + fprintf(stderr, " ip route { add | del | change | append | replace | monitor } ROUTE\n"); + fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); + fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); + fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); + fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); + fprintf(stderr, "NH := [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); + fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ]\n"); + fprintf(stderr, " [ rtt NUMBER ] [ rttvar NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ ssthresh NUMBER ]\n"); + fprintf(stderr, " [ realms REALM ]\n"); + fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); + fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAGS := [ equalize ]\n"); + fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); + fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n"); + exit(-1); +} + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + inet_prefix src; + inet_prefix prefsrc; + inet_prefix via; + int host_len = -1; + SPRINT_BUF(b1); + + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) + return 0; + } else { + if (r->rtm_flags&RTM_F_CLONED) + return 0; + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) + return 0; + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) + return 0; + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) + return 0; + } + if ((filter.protocol^r->rtm_protocol)&filter.protocolmask) + return 0; + if ((filter.scope^r->rtm_scope)&filter.scopemask) + return 0; + if ((filter.type^r->rtm_type)&filter.typemask) + return 0; + if ((filter.tos^r->rtm_tos)&filter.tosmask) + return 0; + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) + return 0; + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) + return 0; + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) + return 0; + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) + return 0; + if (filter.rvia.family && r->rtm_family != filter.rvia.family) + return 0; + if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) + return 0; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + memset(&dst, 0, sizeof(dst)); + dst.family = r->rtm_family; + if (tb[RTA_DST]) + memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); + if (filter.rsrc.family || filter.msrc.family) { + memset(&src, 0, sizeof(src)); + src.family = r->rtm_family; + if (tb[RTA_SRC]) + memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); + } + if (filter.rvia.bitlen>0) { + memset(&via, 0, sizeof(via)); + via.family = r->rtm_family; + if (tb[RTA_GATEWAY]) + memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len); + } + if (filter.rprefsrc.bitlen>0) { + memset(&prefsrc, 0, sizeof(prefsrc)); + prefsrc.family = r->rtm_family; + if (tb[RTA_PREFSRC]) + memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len); + } + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen)) + return 0; + if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen)) + return 0; + if (filter.realmmask) { + __u32 realms = 0; + if (tb[RTA_FLOW]) + realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + if ((realms^filter.realm)&filter.realmmask) + return 0; + } + if (filter.iifmask) { + int iif = 0; + if (tb[RTA_IIF]) + iif = *(int*)RTA_DATA(tb[RTA_IIF]); + if ((iif^filter.iif)&filter.iifmask) + return 0; + } + if (filter.oifmask) { + int oif = 0; + if (tb[RTA_OIF]) + oif = *(int*)RTA_DATA(tb[RTA_OIF]); + if ((oif^filter.oif)&filter.oifmask) + return 0; + } + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) + fprintf(fp, "Deleted "); + if (r->rtm_type != RTN_UNICAST && !filter.type) + fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "%s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "0/%d ", r->rtm_dst_len); + } else { + fprintf(fp, "default "); + } + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%u ", r->rtm_src_len); + } + if (r->rtm_tos && filter.tosmask != -1) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { + fprintf(fp, "via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) + fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + + if (!(r->rtm_flags&RTM_F_CLONED)) { + if (r->rtm_table != RT_TABLE_MAIN && !filter.tb) + fprintf(fp, " table %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + if (r->rtm_protocol != RTPROT_BOOT && filter.protocolmask != -1) + fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + if (r->rtm_scope != RT_SCOPE_UNIVERSE && filter.scopemask != -1) + fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + } + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + fprintf(fp, " src %s ", + rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_PREFSRC]), + RTA_DATA(tb[RTA_PREFSRC]), + abuf, sizeof(abuf))); + } + if (tb[RTA_PRIORITY]) + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + if (r->rtm_flags & RTNH_F_DEAD) + fprintf(fp, "dead "); + if (r->rtm_flags & RTNH_F_ONLINK) + fprintf(fp, "onlink "); + if (r->rtm_flags & RTNH_F_PERVASIVE) + fprintf(fp, "pervasive "); + if (r->rtm_flags & RTM_F_EQUALIZE) + fprintf(fp, "equalize "); + if (r->rtm_flags & RTM_F_NOTIFY) + fprintf(fp, "notify "); + + if (tb[RTA_FLOW] && filter.realmmask != ~0U) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + fprintf(fp, "realm%s ", from ? "s" : ""); + if (from) { + fprintf(fp, "%s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) { + __u32 flags = r->rtm_flags&~0xFFFF; + int first = 1; + + fprintf(fp, "%s cache ", _SL_); + +#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ + flags &= ~RTCF_##fl; \ + fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ + first = 0; } + PRTFL(LOCAL, "local"); + PRTFL(REJECT, "reject"); + PRTFL(MULTICAST, "mc"); + PRTFL(BROADCAST, "brd"); + PRTFL(DNAT, "dst-nat"); + PRTFL(SNAT, "src-nat"); + PRTFL(MASQ, "masq"); + PRTFL(DIRECTDST, "dst-direct"); + PRTFL(DIRECTSRC, "src-direct"); + PRTFL(REDIRECTED, "redirected"); + PRTFL(DOREDIRECT, "redirect"); + PRTFL(FAST, "fastroute"); + PRTFL(NOTIFY, "notify"); + PRTFL(TPROXY, "proxy"); +#ifdef RTCF_EQUALIZE + PRTFL(EQUALIZE, "equalize"); +#endif + if (flags) + fprintf(fp, "%s%x> ", first ? "<" : "", flags); + if (tb[RTA_CACHEINFO]) { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + static int hz; + if (!hz) + hz = get_user_hz(); + if (ci->rta_expires != 0) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + 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 + } + } else if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) + ci = RTA_DATA(tb[RTA_CACHEINFO]); + if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { + static int hz; + if (!hz) + hz = get_user_hz(); + if (r->rtm_flags & RTM_F_CLONED) + fprintf(fp, "%s cache ", _SL_); + if (ci->rta_expires) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + } + } + if (tb[RTA_METRICS]) { + int i; + unsigned mxlock = 0; + struct rtattr *mxrta[RTAX_MAX+1]; + + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + if (mxrta[RTAX_LOCK]) + mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + + for (i=2; i<=RTAX_MAX; i++) { + static char *mx_names[] = + { + "mtu", + "window", + "rtt", + "rttvar", + "ssthresh", + "cwnd", + "advmss", + "reordering", + }; + static int hz; + if (mxrta[i] == NULL) + continue; + if (!hz) + hz = get_hz(); + if (i-2 < sizeof(mx_names)/sizeof(char*)) + fprintf(fp, " %s", mx_names[i-2]); + else + fprintf(fp, " metric %d", i); + if (mxlock & (1<<i)) + fprintf(fp, " lock"); + + if (i != RTAX_RTT && i != RTAX_RTTVAR) + fprintf(fp, " %u", *(unsigned*)RTA_DATA(mxrta[i])); + else { + unsigned val = *(unsigned*)RTA_DATA(mxrta[i]); + + val *= 1000; + if (i == RTAX_RTT) + val /= 8; + else + val /= 4; + if (val >= hz) + fprintf(fp, " %ums", val/hz); + else + fprintf(fp, " %.2fms", (float)val/hz); + } + } + } + if (tb[RTA_IIF] && filter.iifmask != -1) { + fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + } + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); + int first = 0; + + len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + + for (;;) { + if (len < sizeof(*nh)) + break; + if (nh->rtnh_len > len) + break; + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + if (first) + fprintf(fp, " Oifs:"); + else + fprintf(fp, " "); + } else + fprintf(fp, "%s\tnexthop", _SL_); + if (nh->rtnh_len > sizeof(*nh)) { + parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); + if (tb[RTA_GATEWAY]) { + fprintf(fp, " via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + } + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex)); + if (nh->rtnh_hops != 1) + fprintf(fp, "(ttl>%d)", nh->rtnh_hops); + } else { + fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); + fprintf(fp, " weight %d", nh->rtnh_hops+1); + } + if (nh->rtnh_flags & RTNH_F_DEAD) + fprintf(fp, " dead"); + if (nh->rtnh_flags & RTNH_F_ONLINK) + fprintf(fp, " onlink"); + if (nh->rtnh_flags & RTNH_F_PERVASIVE) + fprintf(fp, " pervasive"); + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +int parse_one_nh(struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (++argv, --argc > 0) { + if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + rta_addattr32(rta, 4096, RTA_GATEWAY, get_addr32(*argv)); + rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", *argv); + exit(1); + } + } else if (strcmp(*argv, "weight") == 0) { + unsigned w; + NEXT_ARG(); + if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", *argv); + rtnh->rtnh_hops = w - 1; + } else if (strcmp(*argv, "onlink") == 0) { + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } else + break; + } + *argcp = argc; + *argvp = argv; + return 0; +} + +int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv) +{ + char buf[1024]; + struct rtattr *rta = (void*)buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + while (argc > 0) { + if (strcmp(*argv, "nexthop") != 0) { + fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv); + exit(-1); + } + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); + exit(-1); + } + memset(rtnh, 0, sizeof(*rtnh)); + rtnh->rtnh_len = sizeof(*rtnh); + rtnh->rtnh_ifindex = 0; + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rta->rta_len += rtnh->rtnh_len; + parse_one_nh(rta, rtnh, &argc, &argv); + rtnh = RTNH_NEXT(rtnh); + } + + if (rta->rta_len > RTA_LENGTH(0)) + addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); + return 0; +} + + +int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char mxbuf[256]; + struct rtattr * mxrta = (void*)mxbuf; + unsigned mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int nhs_ok = 0; + int scope_ok = 0; + int table_ok = 0; + int proto_ok = 0; + int type_ok = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + matches(*argv, "preference") == 0) { + __u32 metric; + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid \"scope\" value\n", *argv); + req.r.rtm_scope = scope; + scope_ok = 1; + } else if (strcmp(*argv, "mtu") == 0) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) + invarg("\"mtu\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); +#ifdef RTAX_ADVMSS + } else if (strcmp(*argv, "advmss") == 0) { + unsigned mss; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_ADVMSS); + NEXT_ARG(); + } + 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(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_REORDERING); + NEXT_ARG(); + } + 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(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTT); + NEXT_ARG(); + } + if (get_unsigned(&rtt, *argv, 0)) + invarg("\"rtt\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, rtt); + } else if (matches(*argv, "window") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_WINDOW); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"window\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); + } else if (matches(*argv, "cwnd") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_CWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"cwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); + } else if (matches(*argv, "rttvar") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTTVAR); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"rttvar\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, win); + } else if (matches(*argv, "ssthresh") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_SSTHRESH); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"ssthresh\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("\"realm\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (strcmp(*argv, "onlink") == 0) { + req.r.rtm_flags |= RTNH_F_ONLINK; + } else if (matches(*argv, "equalize") == 0 || + strcmp(*argv, "eql") == 0) { + req.r.rtm_flags |= RTM_F_EQUALIZE; + } else if (strcmp(*argv, "nexthop") == 0) { + nhs_ok = 1; + break; + } else if (matches(*argv, "protocol") == 0) { + int prot; + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + proto_ok =1; + } else if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("\"table\" value is invalid\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = dst.family; + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (d || nhs_ok) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (nhs_ok) + parse_nexthops(&req.n, &req.r, argc, argv); + + if (!table_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_NAT || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_table = RT_TABLE_LOCAL; + } + if (!scope_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_NAT) + req.r.rtm_scope = RT_SCOPE_HOST; + else if (req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_MULTICAST || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_scope = RT_SCOPE_LINK; + else if (req.r.rtm_type == RTN_UNICAST || + req.r.rtm_type == RTN_UNSPEC) { + if (cmd == RTM_DELROUTE) + req.r.rtm_scope = RT_SCOPE_NOWHERE; + else if (!gw_ok && !nhs_ok) + req.r.rtm_scope = RT_SCOPE_LINK; + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + tid = 0; + } else if (strcmp(*argv, "cache") == 0) { + tid = -1; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } + filter.tb = tid; + } else if (matches(*argv, "cached") == 0 || + matches(*argv, "cloned") == 0) { + filter.tb = -1; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = -1; + } else if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (matches(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (matches(*argv, "type") == 0) { + int type; + NEXT_ARG(); + filter.typemask = -1; + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("node type value is invalid\n", *argv); + filter.type = type; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + get_prefix(&filter.rvia, *argv, do_ipv6); + } else if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.rprefsrc, *argv, do_ipv6); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + filter.realm = realm; + filter.realmmask = ~0U; + if ((filter.realm&0xFFFF) == 0 && + (*argv)[strlen(*argv) - 1] == '/') + filter.realmmask &= ~0xFFFF; + if ((filter.realm&0xFFFF0000U) == 0 && + (strchr(*argv, '/') == NULL || + (*argv)[0] == '/')) + filter.realmmask &= ~0xFFFF0000U; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) + do_ipv6 = AF_INET; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (id || od) { + int idx; + + if (id) { + if ((idx = ll_name_to_index(id)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", id); + return -1; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", od); + return -1; + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + time_t start = time(0); + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) { + iproute_flush_cache(); + if (show_stats) + printf("*** IPv4 routing cache is flushed.\n"); + } + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + if (filter.tb != -1 || do_ipv6 == AF_INET6) + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + + if (time(0) - start > 30) { + printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", + time(0) - start, filter.flushed); + exit(1); + } + + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.tb != -1) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } else { + if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } + + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + exit(0); +} + + +int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char *idev = NULL; + char *odev = NULL; + int connected = 0; + int from_ok = 0; + + memset(&req, 0, sizeof(req)); + + iproute_reset_filter(); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = preferred_family; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + while (argc > 0) { + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (matches(*argv, "iif") == 0) { + NEXT_ARG(); + idev = *argv; + } else if (matches(*argv, "oif") == 0 || + strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + odev = *argv; + } else if (matches(*argv, "notify") == 0) { + req.r.rtm_flags |= RTM_F_NOTIFY; + } else if (matches(*argv, "connected") == 0) { + connected = 1; + } else { + inet_prefix addr; + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + + if (req.r.rtm_dst_len == 0) { + fprintf(stderr, "need at least destination address\n"); + exit(1); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", idev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + if ((idx = ll_name_to_index(odev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", odev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + int len = req.n.nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "Not a route?\n"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + fprintf(stderr, "Failed to connect the route\n"); + return -1; + } + if (!odev && tb[RTA_OIF]) + tb[RTA_OIF]->rta_type = 0; + if (tb[RTA_GATEWAY]) + tb[RTA_GATEWAY]->rta_type = 0; + if (!idev && tb[RTA_IIF]) + tb[RTA_IIF]->rta_type = 0; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + exit(0); +} + +void iproute_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +int do_iproute(int argc, char **argv) +{ + if (argc < 1) + return iproute_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "prepend") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, + argc-1, argv+1); + if (matches(*argv, "append") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, + argc-1, argv+1); + if (matches(*argv, "test") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iproute_modify(RTM_DELROUTE, 0, + argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return iproute_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return iproute_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return iproute_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iproute.c.initvar b/ip/iproute.c.initvar new file mode 100644 index 0000000..b2ddb6e --- /dev/null +++ b/ip/iproute.c.initvar @@ -0,0 +1,1413 @@ +/* + * iproute.c "ip route". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <linux/in_route.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n"); + fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); + fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); + fprintf(stderr, " ip route { add | del | change | append | replace | monitor } ROUTE\n"); + fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); + fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); + fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); + fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); + fprintf(stderr, "NH := [ via ADDRESS ] [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); + fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ]\n"); + fprintf(stderr, " [ rtt NUMBER ] [ rttvar NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ ssthresh NUMBER ]\n"); + fprintf(stderr, " [ realms REALM ]\n"); + fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); + fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); + fprintf(stderr, "FLAGS := [ equalize ]\n"); + fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); + fprintf(stderr, "RTPROTO := [ kernel | boot | static | NUMBER ]\n"); + exit(-1); +} + + +static struct +{ + int tb; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; + int protocol, protocolmask; + int scope, scopemask; + int type, typemask; + int tos, tosmask; + int iif, iifmask; + int oif, oifmask; + int realm, realmmask; + inet_prefix rprefsrc; + inet_prefix rvia; + inet_prefix rdst; + inet_prefix mdst; + inet_prefix rsrc; + inet_prefix msrc; +} filter; + +static int flush_update(void) +{ + if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) { + perror("Failed to send flush request\n"); + return -1; + } + filter.flushp = 0; + return 0; +} + +int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + inet_prefix dst; + inet_prefix src; + inet_prefix prefsrc; + inet_prefix via; + int host_len = -1; + SPRINT_BUF(b1); + + + if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { + fprintf(stderr, "Not a route: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) + return 0; + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (r->rtm_family == AF_INET6) { + if (filter.tb) { + if (filter.tb < 0) { + if (!(r->rtm_flags&RTM_F_CLONED)) + return 0; + } else { + if (r->rtm_flags&RTM_F_CLONED) + return 0; + if (filter.tb == RT_TABLE_LOCAL) { + if (r->rtm_type != RTN_LOCAL) + return 0; + } else if (filter.tb == RT_TABLE_MAIN) { + if (r->rtm_type == RTN_LOCAL) + return 0; + } else { + return 0; + } + } + } + } else { + if (filter.tb > 0 && filter.tb != r->rtm_table) + return 0; + } + if ((filter.protocol^r->rtm_protocol)&filter.protocolmask) + return 0; + if ((filter.scope^r->rtm_scope)&filter.scopemask) + return 0; + if ((filter.type^r->rtm_type)&filter.typemask) + return 0; + if ((filter.tos^r->rtm_tos)&filter.tosmask) + return 0; + if (filter.rdst.family && + (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) + return 0; + if (filter.mdst.family && + (r->rtm_family != filter.mdst.family || + (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) + return 0; + if (filter.rsrc.family && + (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) + return 0; + if (filter.msrc.family && + (r->rtm_family != filter.msrc.family || + (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) + return 0; + if (filter.rvia.family && r->rtm_family != filter.rvia.family) + return 0; + if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) + return 0; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + memset(&dst, 0, sizeof(dst)); + dst.family = r->rtm_family; + if (tb[RTA_DST]) + memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); + if (filter.rsrc.family || filter.msrc.family) { + memset(&src, 0, sizeof(src)); + src.family = r->rtm_family; + if (tb[RTA_SRC]) + memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); + } + if (filter.rvia.bitlen>0) { + memset(&via, 0, sizeof(via)); + via.family = r->rtm_family; + if (tb[RTA_GATEWAY]) + memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len); + } + if (filter.rprefsrc.bitlen>0) { + memset(&prefsrc, 0, sizeof(prefsrc)); + prefsrc.family = r->rtm_family; + if (tb[RTA_PREFSRC]) + memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len); + } + + if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen)) + return 0; + if (filter.mdst.family && filter.mdst.bitlen >= 0 && + inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) + return 0; + + if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen)) + return 0; + if (filter.msrc.family && filter.msrc.bitlen >= 0 && + inet_addr_match(&src, &filter.msrc, r->rtm_src_len)) + return 0; + + if (filter.rvia.family && inet_addr_match(&via, &filter.rvia, filter.rvia.bitlen)) + return 0; + if (filter.rprefsrc.family && inet_addr_match(&prefsrc, &filter.rprefsrc, filter.rprefsrc.bitlen)) + return 0; + if (filter.realmmask) { + __u32 realms = 0; + if (tb[RTA_FLOW]) + realms = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + if ((realms^filter.realm)&filter.realmmask) + return 0; + } + if (filter.iifmask) { + int iif = 0; + if (tb[RTA_IIF]) + iif = *(int*)RTA_DATA(tb[RTA_IIF]); + if ((iif^filter.iif)&filter.iifmask) + return 0; + } + if (filter.oifmask) { + int oif = 0; + if (tb[RTA_OIF]) + oif = *(int*)RTA_DATA(tb[RTA_OIF]); + if ((oif^filter.oif)&filter.oifmask) + return 0; + } + if (filter.flushb && + r->rtm_family == AF_INET6 && + r->rtm_dst_len == 0 && + r->rtm_type == RTN_UNREACHABLE && + tb[RTA_PRIORITY] && + *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + return 0; + + if (filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELROUTE; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++filter.rth->seq; + filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushed++; + if (show_stats < 2) + return 0; + } + + if (n->nlmsg_type == RTM_DELROUTE) + fprintf(fp, "Deleted "); + if (r->rtm_type != RTN_UNICAST && !filter.type) + fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "%s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "0/%d ", r->rtm_dst_len); + } else { + fprintf(fp, "default "); + } + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%u ", r->rtm_src_len); + } + if (r->rtm_tos && filter.tosmask != -1) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { + fprintf(fp, "via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + if (tb[RTA_OIF] && filter.oifmask != -1) + fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + + if (!(r->rtm_flags&RTM_F_CLONED)) { + if (r->rtm_table != RT_TABLE_MAIN && !filter.tb) + fprintf(fp, " table %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + if (r->rtm_protocol != RTPROT_BOOT && filter.protocolmask != -1) + fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + if (r->rtm_scope != RT_SCOPE_UNIVERSE && filter.scopemask != -1) + fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + } + if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { + /* Do not use format_host(). It is our local addr + and symbolic name will not be useful. + */ + fprintf(fp, " src %s ", + rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_PREFSRC]), + RTA_DATA(tb[RTA_PREFSRC]), + abuf, sizeof(abuf))); + } + if (tb[RTA_PRIORITY]) + fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); + if (r->rtm_flags & RTNH_F_DEAD) + fprintf(fp, "dead "); + if (r->rtm_flags & RTNH_F_ONLINK) + fprintf(fp, "onlink "); + if (r->rtm_flags & RTNH_F_PERVASIVE) + fprintf(fp, "pervasive "); + if (r->rtm_flags & RTM_F_EQUALIZE) + fprintf(fp, "equalize "); + if (r->rtm_flags & RTM_F_NOTIFY) + fprintf(fp, "notify "); + + if (tb[RTA_FLOW] && filter.realmmask != ~0U) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + fprintf(fp, "realm%s ", from ? "s" : ""); + if (from) { + fprintf(fp, "%s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + if ((r->rtm_flags&RTM_F_CLONED) && r->rtm_family == AF_INET) { + __u32 flags = r->rtm_flags&~0xFFFF; + int first = 1; + + fprintf(fp, "%s cache ", _SL_); + +#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ + flags &= ~RTCF_##fl; \ + fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ + first = 0; } + PRTFL(LOCAL, "local"); + PRTFL(REJECT, "reject"); + PRTFL(MULTICAST, "mc"); + PRTFL(BROADCAST, "brd"); + PRTFL(DNAT, "dst-nat"); + PRTFL(SNAT, "src-nat"); + PRTFL(MASQ, "masq"); + PRTFL(DIRECTDST, "dst-direct"); + PRTFL(DIRECTSRC, "src-direct"); + PRTFL(REDIRECTED, "redirected"); + PRTFL(DOREDIRECT, "redirect"); + PRTFL(FAST, "fastroute"); + PRTFL(NOTIFY, "notify"); + PRTFL(TPROXY, "proxy"); +#ifdef RTCF_EQUALIZE + PRTFL(EQUALIZE, "equalize"); +#endif + if (flags) + fprintf(fp, "%s%x> ", first ? "<" : "", flags); + if (tb[RTA_CACHEINFO]) { + struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + static int hz; + if (!hz) + hz = get_user_hz(); + if (ci->rta_expires != 0) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + 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 + } + } else if (r->rtm_family == AF_INET6) { + struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) + ci = RTA_DATA(tb[RTA_CACHEINFO]); + if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { + static int hz; + if (!hz) + hz = get_user_hz(); + if (r->rtm_flags & RTM_F_CLONED) + fprintf(fp, "%s cache ", _SL_); + if (ci->rta_expires) + fprintf(fp, " expires %dsec", ci->rta_expires/hz); + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + if (show_stats) { + if (ci->rta_clntref) + fprintf(fp, " users %d", ci->rta_clntref); + if (ci->rta_used != 0) + fprintf(fp, " used %d", ci->rta_used); + if (ci->rta_lastuse != 0) + fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + } + } else if (ci) { + if (ci->rta_error != 0) + fprintf(fp, " error %d", ci->rta_error); + } + } + if (tb[RTA_METRICS]) { + int i; + unsigned mxlock = 0; + struct rtattr *mxrta[RTAX_MAX+1]; + + parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + if (mxrta[RTAX_LOCK]) + mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + + for (i=2; i<=RTAX_MAX; i++) { + static char *mx_names[] = + { + "mtu", + "window", + "rtt", + "rttvar", + "ssthresh", + "cwnd", + "advmss", + "reordering", + }; + static int hz; + if (mxrta[i] == NULL) + continue; + if (!hz) + hz = get_hz(); + if (i-2 < sizeof(mx_names)/sizeof(char*)) + fprintf(fp, " %s", mx_names[i-2]); + else + fprintf(fp, " metric %d", i); + if (mxlock & (1<<i)) + fprintf(fp, " lock"); + + if (i != RTAX_RTT && i != RTAX_RTTVAR) + fprintf(fp, " %u", *(unsigned*)RTA_DATA(mxrta[i])); + else { + unsigned val = *(unsigned*)RTA_DATA(mxrta[i]); + + val *= 1000; + if (i == RTAX_RTT) + val /= 8; + else + val /= 4; + if (val >= hz) + fprintf(fp, " %ums", val/hz); + else + fprintf(fp, " %.2fms", (float)val/hz); + } + } + } + if (tb[RTA_IIF] && filter.iifmask != -1) { + fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + } + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); + int first = 0; + + len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + + for (;;) { + if (len < sizeof(*nh)) + break; + if (nh->rtnh_len > len) + break; + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + if (first) + fprintf(fp, " Oifs:"); + else + fprintf(fp, " "); + } else + fprintf(fp, "%s\tnexthop", _SL_); + if (nh->rtnh_len > sizeof(*nh)) { + parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); + if (tb[RTA_GATEWAY]) { + fprintf(fp, " via %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } + } + if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { + fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex)); + if (nh->rtnh_hops != 1) + fprintf(fp, "(ttl>%d)", nh->rtnh_hops); + } else { + fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); + fprintf(fp, " weight %d", nh->rtnh_hops+1); + } + if (nh->rtnh_flags & RTNH_F_DEAD) + fprintf(fp, " dead"); + if (nh->rtnh_flags & RTNH_F_ONLINK) + fprintf(fp, " onlink"); + if (nh->rtnh_flags & RTNH_F_PERVASIVE) + fprintf(fp, " pervasive"); + len -= NLMSG_ALIGN(nh->rtnh_len); + nh = RTNH_NEXT(nh); + } + } + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + + +int parse_one_nh(struct rtattr *rta, struct rtnexthop *rtnh, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + while (++argv, --argc > 0) { + if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + rta_addattr32(rta, 4096, RTA_GATEWAY, get_addr32(*argv)); + rtnh->rtnh_len += sizeof(struct rtattr) + 4; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", *argv); + exit(1); + } + } else if (strcmp(*argv, "weight") == 0) { + unsigned w; + NEXT_ARG(); + if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) + invarg("\"weight\" is invalid\n", *argv); + rtnh->rtnh_hops = w - 1; + } else if (strcmp(*argv, "onlink") == 0) { + rtnh->rtnh_flags |= RTNH_F_ONLINK; + } else + break; + } + *argcp = argc; + *argvp = argv; + return 0; +} + +int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv) +{ + char buf[1024]; + struct rtattr *rta = (void*)buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH(0); + rtnh = RTA_DATA(rta); + + while (argc > 0) { + if (strcmp(*argv, "nexthop") != 0) { + fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", *argv); + exit(-1); + } + if (argc <= 1) { + fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); + exit(-1); + } + memset(rtnh, 0, sizeof(*rtnh)); + rtnh->rtnh_len = sizeof(*rtnh); + rta->rta_len += rtnh->rtnh_len; + parse_one_nh(rta, rtnh, &argc, &argv); + rtnh = RTNH_NEXT(rtnh); + } + + if (rta->rta_len > RTA_LENGTH(0)) + addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); + return 0; +} + + +int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char mxbuf[256]; + struct rtattr * mxrta = (void*)mxbuf; + unsigned mxlock = 0; + char *d = NULL; + int gw_ok = 0; + int dst_ok = 0; + int nhs_ok = 0; + int scope_ok = 0; + int table_ok = 0; + int proto_ok = 0; + int type_ok = 0; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.r.rtm_family = preferred_family; + req.r.rtm_table = RT_TABLE_MAIN; + req.r.rtm_scope = RT_SCOPE_NOWHERE; + + if (cmd != RTM_DELROUTE) { + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_type = RTN_UNICAST; + } + + mxrta->rta_type = RTA_METRICS; + mxrta->rta_len = RTA_LENGTH(0); + + while (argc > 0) { + if (strcmp(*argv, "src") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + gw_ok = 1; + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + } else if (strcmp(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("\"tos\" value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "metric") == 0 || + matches(*argv, "priority") == 0 || + matches(*argv, "preference") == 0) { + __u32 metric; + NEXT_ARG(); + if (get_u32(&metric, *argv, 0)) + invarg("\"metric\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); + } else if (strcmp(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + if (rtnl_rtscope_a2n(&scope, *argv)) + invarg("invalid \"scope\" value\n", *argv); + req.r.rtm_scope = scope; + scope_ok = 1; + } else if (strcmp(*argv, "mtu") == 0) { + unsigned mtu; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_MTU); + NEXT_ARG(); + } + if (get_unsigned(&mtu, *argv, 0)) + invarg("\"mtu\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); +#ifdef RTAX_ADVMSS + } else if (strcmp(*argv, "advmss") == 0) { + unsigned mss; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_ADVMSS); + NEXT_ARG(); + } + 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(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_REORDERING); + NEXT_ARG(); + } + 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(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTT); + NEXT_ARG(); + } + if (get_unsigned(&rtt, *argv, 0)) + invarg("\"rtt\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, rtt); + } else if (matches(*argv, "window") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_WINDOW); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"window\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); + } else if (matches(*argv, "cwnd") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_CWND); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"cwnd\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); + } else if (matches(*argv, "rttvar") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_RTTVAR); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"rttvar\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, win); + } else if (matches(*argv, "ssthresh") == 0) { + unsigned win; + NEXT_ARG(); + if (strcmp(*argv, "lock") == 0) { + mxlock |= (1<<RTAX_SSTHRESH); + NEXT_ARG(); + } + if (get_unsigned(&win, *argv, 0)) + invarg("\"ssthresh\" value is invalid\n", *argv); + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("\"realm\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (strcmp(*argv, "onlink") == 0) { + req.r.rtm_flags |= RTNH_F_ONLINK; + } else if (matches(*argv, "equalize") == 0 || + strcmp(*argv, "eql") == 0) { + req.r.rtm_flags |= RTM_F_EQUALIZE; + } else if (strcmp(*argv, "nexthop") == 0) { + nhs_ok = 1; + break; + } else if (matches(*argv, "protocol") == 0) { + int prot; + NEXT_ARG(); + if (rtnl_rtprot_a2n(&prot, *argv)) + invarg("\"protocol\" value is invalid\n", *argv); + req.r.rtm_protocol = prot; + proto_ok =1; + } else if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("\"table\" value is invalid\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + d = *argv; + } else { + int type; + inet_prefix dst; + + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if ((**argv < '0' || **argv > '9') && + rtnl_rtntype_a2n(&type, *argv) == 0) { + NEXT_ARG(); + req.r.rtm_type = type; + type_ok = 1; + } + + if (matches(*argv, "help") == 0) + usage(); + if (dst_ok) + duparg2("to", *argv); + get_prefix(&dst, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = dst.family; + req.r.rtm_dst_len = dst.bitlen; + dst_ok = 1; + if (dst.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } + argc--; argv++; + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (d || nhs_ok) { + int idx; + + ll_init_map(&rth); + + if (d) { + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (mxrta->rta_len > RTA_LENGTH(0)) { + if (mxlock) + rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); + addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); + } + + if (nhs_ok) + parse_nexthops(&req.n, &req.r, argc, argv); + + if (!table_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_NAT || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_table = RT_TABLE_LOCAL; + } + if (!scope_ok) { + if (req.r.rtm_type == RTN_LOCAL || + req.r.rtm_type == RTN_NAT) + req.r.rtm_scope = RT_SCOPE_HOST; + else if (req.r.rtm_type == RTN_BROADCAST || + req.r.rtm_type == RTN_MULTICAST || + req.r.rtm_type == RTN_ANYCAST) + req.r.rtm_scope = RT_SCOPE_LINK; + else if (req.r.rtm_type == RTN_UNICAST || + req.r.rtm_type == RTN_UNSPEC) { + if (cmd == RTM_DELROUTE) + req.r.rtm_scope = RT_SCOPE_NOWHERE; + else if (!gw_ok && !nhs_ok) + req.r.rtm_scope = RT_SCOPE_LINK; + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) +{ + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + memset(&req, 0, sizeof(req)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.rtm.rtm_family = family; + req.rtm.rtm_flags |= RTM_F_CLONED; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +static int iproute_flush_cache(void) +{ +#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" + + int len; + int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + char *buffer = "-1"; + + if (flush_fd < 0) { + fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH); + return -1; + } + + len = strlen (buffer); + + if ((write (flush_fd, (void *)buffer, len)) < len) { + fprintf (stderr, "Cannot flush routing cache\n"); + return -1; + } + close(flush_fd); + return 0; +} + + +static int iproute_list_or_flush(int argc, char **argv, int flush) +{ + int do_ipv6 = preferred_family; + struct rtnl_handle rth; + char *id = NULL; + char *od = NULL; + + iproute_reset_filter(); + filter.tb = RT_TABLE_MAIN; + + if (flush && argc <= 0) { + fprintf(stderr, "\"ip route flush\" requires arguments.\n"); + return -1; + } + + while (argc > 0) { + if (matches(*argv, "table") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) { + if (strcmp(*argv, "all") == 0) { + tid = 0; + } else if (strcmp(*argv, "cache") == 0) { + tid = -1; + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("table id value is invalid\n", *argv); + } + } + filter.tb = tid; + } else if (matches(*argv, "cached") == 0 || + matches(*argv, "cloned") == 0) { + filter.tb = -1; + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + filter.tos = tos; + filter.tosmask = -1; + } else if (matches(*argv, "protocol") == 0) { + int prot = 0; + NEXT_ARG(); + filter.protocolmask = -1; + if (rtnl_rtprot_a2n(&prot, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"protocol\"\n", *argv); + prot = 0; + filter.protocolmask = 0; + } + filter.protocol = prot; + } else if (matches(*argv, "scope") == 0) { + int scope = 0; + NEXT_ARG(); + filter.scopemask = -1; + if (rtnl_rtscope_a2n(&scope, *argv)) { + if (strcmp(*argv, "all") != 0) + invarg("invalid \"scope\"\n", *argv); + scope = RT_SCOPE_NOWHERE; + filter.scopemask = 0; + } + filter.scope = scope; + } else if (matches(*argv, "type") == 0) { + int type; + NEXT_ARG(); + filter.typemask = -1; + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("node type value is invalid\n", *argv); + filter.type = type; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + od = *argv; + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + id = *argv; + } else if (strcmp(*argv, "via") == 0) { + NEXT_ARG(); + get_prefix(&filter.rvia, *argv, do_ipv6); + } else if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + get_prefix(&filter.rprefsrc, *argv, do_ipv6); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + filter.realm = realm; + filter.realmmask = ~0U; + if ((filter.realm&0xFFFF) == 0 && + (*argv)[strlen(*argv) - 1] == '/') + filter.realmmask &= ~0xFFFF; + if ((filter.realm&0xFFFF0000U) == 0 && + (strchr(*argv, '/') == NULL || + (*argv)[0] == '/')) + filter.realmmask &= ~0xFFFF0000U; + } else if (matches(*argv, "from") == 0) { + NEXT_ARG(); + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rsrc, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.msrc, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.msrc, *argv, do_ipv6); + filter.rsrc = filter.msrc; + } + } else { + if (matches(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "root") == 0) { + NEXT_ARG(); + get_prefix(&filter.rdst, *argv, do_ipv6); + } else if (matches(*argv, "match") == 0) { + NEXT_ARG(); + get_prefix(&filter.mdst, *argv, do_ipv6); + } else { + if (matches(*argv, "exact") == 0) { + NEXT_ARG(); + } + get_prefix(&filter.mdst, *argv, do_ipv6); + filter.rdst = filter.mdst; + } + } + argc--; argv++; + } + + if (do_ipv6 == AF_UNSPEC && filter.tb) + do_ipv6 = AF_INET; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (id || od) { + int idx; + + if (id) { + if ((idx = ll_name_to_index(id)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", id); + return -1; + } + filter.iif = idx; + filter.iifmask = -1; + } + if (od) { + if ((idx = ll_name_to_index(od)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", od); + return -1; + } + filter.oif = idx; + filter.oifmask = -1; + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + time_t start = time(0); + + if (filter.tb == -1) { + if (do_ipv6 != AF_INET6) { + iproute_flush_cache(); + if (show_stats) + printf("*** IPv4 routing cache is flushed.\n"); + } + if (do_ipv6 == AF_INET) + return 0; + } + + filter.flushb = flushb; + filter.flushp = 0; + filter.flushe = sizeof(flushb); + filter.rth = &rth; + + for (;;) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + filter.flushed = 0; + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (filter.flushed == 0) { + if (round == 0) { + if (filter.tb != -1 || do_ipv6 == AF_INET6) + fprintf(stderr, "Nothing to flush.\n"); + } else if (show_stats) + printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + fflush(stdout); + return 0; + } + round++; + if (flush_update() < 0) + exit(1); + + if (time(0) - start > 30) { + printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", + time(0) - start, filter.flushed); + exit(1); + } + + if (show_stats) { + printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed); + fflush(stdout); + } + } + } + + if (filter.tb != -1) { + if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } else { + if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { + perror("Cannot send dump request"); + exit(1); + } + } + + if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + exit(0); +} + + +int iproute_get(int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + char *idev = NULL; + char *odev = NULL; + int connected = 0; + int from_ok = 0; + + memset(&req, 0, sizeof(req)); + + iproute_reset_filter(); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + req.r.rtm_family = preferred_family; + req.r.rtm_table = 0; + req.r.rtm_protocol = 0; + req.r.rtm_scope = 0; + req.r.rtm_type = 0; + req.r.rtm_src_len = 0; + req.r.rtm_dst_len = 0; + req.r.rtm_tos = 0; + + while (argc > 0) { + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (matches(*argv, "from") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (matches(*argv, "help") == 0) + usage(); + from_ok = 1; + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); + req.r.rtm_src_len = addr.bitlen; + } else if (matches(*argv, "iif") == 0) { + NEXT_ARG(); + idev = *argv; + } else if (matches(*argv, "oif") == 0 || + strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + odev = *argv; + } else if (matches(*argv, "notify") == 0) { + req.r.rtm_flags |= RTM_F_NOTIFY; + } else if (matches(*argv, "connected") == 0) { + connected = 1; + } else { + inet_prefix addr; + if (strcmp(*argv, "to") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + get_prefix(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + if (addr.bytelen) + addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); + req.r.rtm_dst_len = addr.bitlen; + } + argc--; argv++; + } + + if (req.r.rtm_dst_len == 0) { + fprintf(stderr, "need at least destination address\n"); + exit(1); + } + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + ll_init_map(&rth); + + if (idev || odev) { + int idx; + + if (idev) { + if ((idx = ll_name_to_index(idev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", idev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_IIF, idx); + } + if (odev) { + if ((idx = ll_name_to_index(odev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", odev); + return -1; + } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); + } + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + + if (connected && !from_ok) { + struct rtmsg *r = NLMSG_DATA(&req.n); + int len = req.n.nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + if (req.n.nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "Not a route?\n"); + return -1; + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PREFSRC]) { + tb[RTA_PREFSRC]->rta_type = RTA_SRC; + r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); + } else if (!tb[RTA_SRC]) { + fprintf(stderr, "Failed to connect the route\n"); + return -1; + } + if (!odev && tb[RTA_OIF]) + tb[RTA_OIF]->rta_type = 0; + if (tb[RTA_GATEWAY]) + tb[RTA_GATEWAY]->rta_type = 0; + if (!idev && tb[RTA_IIF]) + tb[RTA_IIF]->rta_type = 0; + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_GETROUTE; + + if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) + exit(2); + } + + if (print_route(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + exit(0); +} + +void iproute_reset_filter() +{ + memset(&filter, 0, sizeof(filter)); + filter.mdst.bitlen = -1; + filter.msrc.bitlen = -1; +} + +int do_iproute(int argc, char **argv) +{ + if (argc < 1) + return iproute_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (matches(*argv, "prepend") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, + argc-1, argv+1); + if (matches(*argv, "append") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, + argc-1, argv+1); + if (matches(*argv, "test") == 0) + return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, + argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return iproute_modify(RTM_DELROUTE, 0, + argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return iproute_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return iproute_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) + return iproute_list_or_flush(argc-1, argv+1, 1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iproute.o b/ip/iproute.o new file mode 100644 index 0000000000000000000000000000000000000000..37f27793f03ac3ce28fa617d82a204f73cf1f97e GIT binary patch literal 44480 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 QB1itN<Q|JJe^>JV0(5iMlK=n! literal 0 HcmV?d00001 diff --git a/ip/iprule.c b/ip/iprule.c new file mode 100644 index 0000000..764edc8 --- /dev/null +++ b/ip/iprule.c @@ -0,0 +1,383 @@ +/* + * iprule.c "ip rule". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <string.h> + +#include "rt_names.h" +#include "utils.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip rule [ list | add | del | flush ] SELECTOR ACTION\n"); + fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n"); + fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n"); + fprintf(stderr, "ACTION := [ table TABLE_ID ]\n"); + fprintf(stderr, " [ prohibit | reject | unreachable ]\n"); + fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); + fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); + exit(-1); +} + +static int print_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + int host_len = -1; + struct rtattr * tb[RTA_MAX+1]; + char abuf[256]; + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_NEWRULE) + return 0; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (r->rtm_family == AF_INET) + host_len = 32; + else if (r->rtm_family == AF_INET6) + host_len = 128; + else if (r->rtm_family == AF_DECnet) + host_len = 16; + else if (r->rtm_family == AF_IPX) + host_len = 80; + + if (tb[RTA_PRIORITY]) + fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])); + else + fprintf(fp, "0:\t"); + + if (tb[RTA_SRC]) { + if (r->rtm_src_len != host_len) { + fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)), + r->rtm_src_len + ); + } else { + fprintf(fp, "from %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_SRC]), + RTA_DATA(tb[RTA_SRC]), + abuf, sizeof(abuf)) + ); + } + } else if (r->rtm_src_len) { + fprintf(fp, "from 0/%d ", r->rtm_src_len); + } else { + fprintf(fp, "from all "); + } + + if (tb[RTA_DST]) { + if (r->rtm_dst_len != host_len) { + fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf)), + r->rtm_dst_len + ); + } else { + fprintf(fp, "to %s ", format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_DST]), + RTA_DATA(tb[RTA_DST]), + abuf, sizeof(abuf))); + } + } else if (r->rtm_dst_len) { + fprintf(fp, "to 0/%d ", r->rtm_dst_len); + } + + if (r->rtm_tos) { + SPRINT_BUF(b1); + fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1))); + } + if (tb[RTA_PROTOINFO]) { + fprintf(fp, "fwmark %#x ", *(__u32*)RTA_DATA(tb[RTA_PROTOINFO])); + } + + if (tb[RTA_IIF]) { + fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF])); + } + + if (r->rtm_table) + fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1))); + + if (tb[RTA_FLOW]) { + __u32 to = *(__u32*)RTA_DATA(tb[RTA_FLOW]); + __u32 from = to>>16; + to &= 0xFFFF; + if (from) { + fprintf(fp, "realms %s/", + rtnl_rtrealm_n2a(from, b1, sizeof(b1))); + } + fprintf(fp, "%s ", + rtnl_rtrealm_n2a(to, b1, sizeof(b1))); + } + + if (r->rtm_type == RTN_NAT) { + if (tb[RTA_GATEWAY]) { + fprintf(fp, "map-to %s ", + format_host(r->rtm_family, + RTA_PAYLOAD(tb[RTA_GATEWAY]), + RTA_DATA(tb[RTA_GATEWAY]), + abuf, sizeof(abuf))); + } else + fprintf(fp, "masquerade"); + } else if (r->rtm_type != RTN_UNICAST) + fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); + + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +static int iprule_list(int argc, char **argv) +{ + struct rtnl_handle rth; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (argc > 0) { + fprintf(stderr, "\"ip rule show\" does not take any arguments.\n"); + return -1; + } + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + + +static int iprule_modify(int cmd, int argc, char **argv) +{ + int table_ok = 0; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[1024]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_type = cmd; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.r.rtm_family = preferred_family; + req.r.rtm_protocol = RTPROT_BOOT; + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_table = 0; + req.r.rtm_type = RTN_UNSPEC; + + if (cmd == RTM_NEWRULE) { + req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; + req.r.rtm_type = RTN_UNICAST; + } + + while (argc > 0) { + if (strcmp(*argv, "from") == 0) { + inet_prefix dst; + NEXT_ARG(); + get_prefix(&dst, *argv, req.r.rtm_family); + req.r.rtm_src_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen); + } else if (strcmp(*argv, "to") == 0) { + inet_prefix dst; + NEXT_ARG(); + get_prefix(&dst, *argv, req.r.rtm_family); + req.r.rtm_dst_len = dst.bitlen; + addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "order") == 0 || + matches(*argv, "priority") == 0) { + __u32 pref; + NEXT_ARG(); + if (get_u32(&pref, *argv, 0)) + invarg("preference value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); + } else if (strcmp(*argv, "tos") == 0) { + __u32 tos; + NEXT_ARG(); + if (rtnl_dsfield_a2n(&tos, *argv)) + invarg("TOS value is invalid\n", *argv); + req.r.rtm_tos = tos; + } else if (strcmp(*argv, "fwmark") == 0) { + __u32 fwmark; + NEXT_ARG(); + if (get_u32(&fwmark, *argv, 0)) + invarg("fwmark value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); + } else if (matches(*argv, "realms") == 0) { + __u32 realm; + NEXT_ARG(); + if (get_rt_realms(&realm, *argv)) + invarg("invalid realms\n", *argv); + addattr32(&req.n, sizeof(req), RTA_FLOW, realm); + } else if (matches(*argv, "table") == 0 || + strcmp(*argv, "lookup") == 0) { + int tid; + NEXT_ARG(); + if (rtnl_rttable_a2n(&tid, *argv)) + invarg("invalid table ID\n", *argv); + req.r.rtm_table = tid; + table_ok = 1; + } else if (strcmp(*argv, "dev") == 0 || + strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1); + } else if (strcmp(*argv, "nat") == 0 || + matches(*argv, "map-to") == 0) { + NEXT_ARG(); + fprintf(stderr, "Warning: route NAT is deprecated\n"); + addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv)); + req.r.rtm_type = RTN_NAT; + } else { + int type; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (rtnl_rtntype_a2n(&type, *argv)) + invarg("Failed to parse rule type", *argv); + req.r.rtm_type = type; + } + argc--; + argv++; + } + + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = AF_INET; + + if (!table_ok && cmd == RTM_NEWRULE) + req.r.rtm_table = RT_TABLE_MAIN; + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + + +static int flush_rule(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + struct rtnl_handle rth; + struct rtmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[RTA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) + return -1; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + if (tb[RTA_PRIORITY]) { + n->nlmsg_type = RTM_DELRULE; + n->nlmsg_flags = NLM_F_REQUEST; + + if (rtnl_open(&rth, 0) < 0) + return -1; + + if (rtnl_talk(&rth, n, 0, 0, NULL, NULL, NULL) < 0) + return -2; + } + + return 0; +} + +static int iprule_flush(int argc, char **argv) +{ + struct rtnl_handle rth; + int af = preferred_family; + + if (af == AF_UNSPEC) + af = AF_INET; + + if (argc > 0) { + fprintf(stderr, "\"ip rule flush\" need not any arguments.\n"); + return -1; + } + + if (rtnl_open(&rth, 0) < 0) + return 1; + + if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, flush_rule, NULL, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + return 1; + } + + return 0; +} + +int do_iprule(int argc, char **argv) +{ + if (argc < 1) { + return iprule_list(0, NULL); + } else if (matches(argv[0], "list") == 0 || + matches(argv[0], "lst") == 0 || + matches(argv[0], "show") == 0) { + return iprule_list(argc-1, argv+1); + } else if (matches(argv[0], "add") == 0) { + return iprule_modify(RTM_NEWRULE, argc-1, argv+1); + } else if (matches(argv[0], "delete") == 0) { + return iprule_modify(RTM_DELRULE, argc-1, argv+1); + } else if (matches(argv[0], "flush") == 0) { + return iprule_flush(argc-1, argv+1); + } else if (matches(argv[0], "help") == 0) + usage(); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip rule help\".\n", *argv); + exit(-1); +} + diff --git a/ip/iprule.o b/ip/iprule.o new file mode 100644 index 0000000000000000000000000000000000000000..f880305ed0ac11f9c556c6e070ef3986b7617072 GIT binary patch literal 11912 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 literal 0 HcmV?d00001 diff --git a/ip/iptunnel.c b/ip/iptunnel.c new file mode 100644 index 0000000..2da3df1 --- /dev/null +++ b/ip/iptunnel.c @@ -0,0 +1,585 @@ +/* + * iptunnel.c "ip tunnel" + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit + * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/ip.h> + +#ifndef __constant_htons +#define __constant_htons(x) htons(x) +#endif + +#include <linux/if_tunnel.h> + +#include "rt_names.h" +#include "utils.h" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip tunnel { add | change | del | show } [ NAME ]\n"); + fprintf(stderr, " [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n"); + fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"); + fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: NAME := STRING\n"); + fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n"); + fprintf(stderr, " TOS := { NUMBER | inherit }\n"); + fprintf(stderr, " TTL := { 1..255 | inherit }\n"); + fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n"); + exit(-1); +} + +static int do_ioctl_get_ifindex(const char *dev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFINDEX, &ifr); + if (err) { + perror("ioctl"); + return 0; + } + close(fd); + return ifr.ifr_ifindex; +} + +static int do_ioctl_get_iftype(const char *dev) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (err) { + perror("ioctl"); + return -1; + } + close(fd); + return ifr.ifr_addr.sa_family; +} + + +static char * do_ioctl_get_ifname(int idx) +{ + static struct ifreq ifr; + int fd; + int err; + + ifr.ifr_ifindex = idx; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFNAME, &ifr); + if (err) { + perror("ioctl"); + return NULL; + } + close(fd); + return ifr.ifr_name; +} + + +static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGETTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (cmd == SIOCCHGTUNNEL && p->name[0]) + strncpy(ifr.ifr_name, p->name, IFNAMSIZ); + else + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, cmd, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + + if (p->name[0]) + strncpy(ifr.ifr_name, p->name, IFNAMSIZ); + else + strncpy(ifr.ifr_name, basedev, IFNAMSIZ); + ifr.ifr_ifru.ifru_data = (void*)p; + fd = socket(AF_INET, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCDELTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) +{ + int count = 0; + char medium[IFNAMSIZ]; + + memset(p, 0, sizeof(*p)); + memset(&medium, 0, sizeof(medium)); + + p->iph.version = 4; + p->iph.ihl = 5; +#ifndef IP_DF +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#endif + p->iph.frag_off = htons(IP_DF); + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "ipip") == 0 || + strcmp(*argv, "ip/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_IPIP; + } else if (strcmp(*argv, "gre") == 0 || + strcmp(*argv, "gre/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_GRE; + } else if (strcmp(*argv, "sit") == 0 || + strcmp(*argv, "ipv6/ip") == 0) { + if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + p->iph.protocol = IPPROTO_IPV6; + } else { + fprintf(stderr,"Cannot guess tunnel mode.\n"); + exit(-1); + } + } 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->o_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, "nopmtudisc") == 0) { + p->iph.frag_off = 0; + } else if (strcmp(*argv, "pmtudisc") == 0) { + p->iph.frag_off = htons(IP_DF); + } else if (strcmp(*argv, "remote") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.daddr = get_addr32(*argv); + } else if (strcmp(*argv, "local") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "any")) + p->iph.saddr = get_addr32(*argv); + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(medium, *argv, IFNAMSIZ-1); + } else if (strcmp(*argv, "ttl") == 0) { + unsigned uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (get_unsigned(&uval, *argv, 0)) + invarg("invalid TTL\n", *argv); + if (uval > 255) + invarg("TTL must be <=255\n", *argv); + p->iph.ttl = uval; + } + } else if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + __u32 uval; + NEXT_ARG(); + if (strcmp(*argv, "inherit") != 0) { + if (rtnl_dsfield_a2n(&uval, *argv)) + invarg("bad TOS value", *argv); + p->iph.tos = uval; + } else + p->iph.tos = 1; + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } + if (matches(*argv, "help") == 0) + usage(); + if (p->name[0]) + duparg2("name", *argv); + strncpy(p->name, *argv, IFNAMSIZ); + if (cmd == SIOCCHGTUNNEL && count == 0) { + struct ip_tunnel_parm old_p; + memset(&old_p, 0, sizeof(old_p)); + if (do_get_ioctl(*argv, &old_p)) + return -1; + *p = old_p; + } + } + count++; + argc--; argv++; + } + + + if (p->iph.protocol == 0) { + if (memcmp(p->name, "gre", 3) == 0) + p->iph.protocol = IPPROTO_GRE; + else if (memcmp(p->name, "ipip", 4) == 0) + p->iph.protocol = IPPROTO_IPIP; + else if (memcmp(p->name, "sit", 3) == 0) + p->iph.protocol = IPPROTO_IPV6; + } + + if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { + if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { + fprintf(stderr, "Keys are not allowed with ipip and sit.\n"); + return -1; + } + } + + if (medium[0]) { + p->link = do_ioctl_get_ifindex(medium); + if (p->link == 0) + return -1; + } + + if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->i_key = p->iph.daddr; + p->i_flags |= GRE_KEY; + } + if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { + p->o_key = p->iph.daddr; + p->o_flags |= GRE_KEY; + } + if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { + fprintf(stderr, "Broadcast tunnel requires a source address.\n"); + return -1; + } + return 0; +} + + +static int do_add(int cmd, int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, cmd, &p) < 0) + return -1; + + if (p.iph.ttl && p.iph.frag_off == 0) { + fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n"); + return -1; + } + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_add_ioctl(cmd, "tunl0", &p); + case IPPROTO_GRE: + return do_add_ioctl(cmd, "gre0", &p); + case IPPROTO_IPV6: + return do_add_ioctl(cmd, "sit0", &p); + default: + fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n"); + return -1; + } + return -1; +} + +int do_del(int argc, char **argv) +{ + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + return do_del_ioctl("tunl0", &p); + case IPPROTO_GRE: + return do_del_ioctl("gre0", &p); + case IPPROTO_IPV6: + return do_del_ioctl("sit0", &p); + default: + return do_del_ioctl(p.name, &p); + } + return -1; +} + +void print_tunnel(struct ip_tunnel_parm *p) +{ + char s1[1024]; + char s2[1024]; + char s3[64]; + char s4[64]; + + inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); + inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); + + /* Do not use format_host() for local addr, + * symbolic name will not be useful. + */ + printf("%s: %s/ip remote %s local %s ", + p->name, + p->iph.protocol == IPPROTO_IPIP ? "ip" : + (p->iph.protocol == IPPROTO_GRE ? "gre" : + (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), + p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any", + p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any"); + + if (p->link) { + char *n = do_ioctl_get_ifname(p->link); + if (n) + printf(" dev %s ", n); + } + + if (p->iph.ttl) + printf(" ttl %d ", p->iph.ttl); + else + printf(" ttl inherit "); + + if (p->iph.tos) { + SPRINT_BUF(b1); + printf(" tos"); + if (p->iph.tos&1) + printf(" inherit"); + if (p->iph.tos&~1) + printf("%c%s ", p->iph.tos&1 ? '/' : ' ', + rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); + } + + if (!(p->iph.frag_off&htons(IP_DF))) + printf(" nopmtudisc"); + + if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) + printf(" key %s", s3); + else if ((p->i_flags|p->o_flags)&GRE_KEY) { + if (p->i_flags&GRE_KEY) + printf(" ikey %s ", s3); + if (p->o_flags&GRE_KEY) + printf(" okey %s ", s4); + } + + 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 do_tunnels_list(struct ip_tunnel_parm *p) +{ + char name[IFNAMSIZ]; + unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, + rx_fifo, rx_frame, + tx_bytes, tx_packets, tx_errs, tx_drops, + tx_fifo, tx_colls, tx_carrier, rx_multi; + int type; + struct ip_tunnel_parm p1; + + char buf[512]; + FILE *fp = fopen("/proc/net/dev", "r"); + if (fp == NULL) { + perror("fopen"); + return -1; + } + + fgets(buf, sizeof(buf), fp); + fgets(buf, sizeof(buf), fp); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *ptr; + buf[sizeof(buf) - 1] = 0; + if ((ptr = strchr(buf, ':')) == NULL || + (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { + fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n"); + return -1; + } + if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", + &rx_bytes, &rx_packets, &rx_errs, &rx_drops, + &rx_fifo, &rx_frame, &rx_multi, + &tx_bytes, &tx_packets, &tx_errs, &tx_drops, + &tx_fifo, &tx_colls, &tx_carrier) != 14) + continue; + if (p->name[0] && strcmp(p->name, name)) + continue; + type = do_ioctl_get_iftype(name); + if (type == -1) { + fprintf(stderr, "Failed to get type of [%s]\n", name); + continue; + } + if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) + continue; + memset(&p1, 0, sizeof(p1)); + if (do_get_ioctl(name, &p1)) + continue; + if ((p->link && p1.link != p->link) || + (p->name[0] && strcmp(p1.name, p->name)) || + (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || + (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || + (p->i_key && p1.i_key != p->i_key)) + continue; + print_tunnel(&p1); + if (show_stats) { + printf("%s", _SL_); + printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", + rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); + printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); + printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", + tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); + } + printf("\n"); + } + return 0; +} + +static int do_show(int argc, char **argv) +{ + int err; + struct ip_tunnel_parm p; + + if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) + return -1; + + switch (p.iph.protocol) { + case IPPROTO_IPIP: + err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); + break; + case IPPROTO_GRE: + err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); + break; + case IPPROTO_IPV6: + err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); + break; + default: + do_tunnels_list(&p); + return 0; + } + if (err) + return -1; + + print_tunnel(&p); + printf("\n"); + return 0; +} + +int do_iptunnel(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc-1, argv+1); + if (matches(*argv, "del") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv); + exit(-1); +} diff --git a/ip/iptunnel.o b/ip/iptunnel.o new file mode 100644 index 0000000000000000000000000000000000000000..4408086aca4f5d719085ed67f58e19a6a2f2ef16 GIT binary patch literal 17440 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 literal 0 HcmV?d00001 diff --git a/ip/ipxfrm.c b/ip/ipxfrm.c new file mode 100644 index 0000000..fc0f0d9 --- /dev/null +++ b/ip/ipxfrm.c @@ -0,0 +1,1051 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on ip.c, iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <time.h> +#include <netdb.h> +#include <net/if.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/xfrm.h> + +#include "utils.h" +#include "xfrm.h" + +struct xfrm_filter filter; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip xfrm XFRM_OBJECT { COMMAND | help }\n" + "where XFRM_OBJECT := { state | policy }\n"); + exit(-1); +} + +/* This is based on utils.c(inet_addr_match) */ +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits) +{ + __u32 *a1 = (__u32 *)x1; + __u32 *a2 = (__u32 *)x2; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +struct typeent { + const char *t_name; + int t_type; +}; + +static const struct typeent xfrmproto_types[]= { + { "esp", IPPROTO_ESP }, { "ah", IPPROTO_AH }, { "comp", IPPROTO_COMP }, + { NULL, -1 } +}; + +int xfrm_xfrmproto_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_xfrmproto(__u8 proto) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &xfrmproto_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == proto) + return t->t_name; + } + + return NULL; +} + +static const struct typeent algo_types[]= { + { "enc", XFRMA_ALG_CRYPT }, { "auth", XFRMA_ALG_AUTH }, + { "comp", XFRMA_ALG_COMP }, { NULL, -1 } +}; + +int xfrm_algotype_getbyname(char *name) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (strcmp(t->t_name, name) == 0) + return t->t_type; + } + + return -1; +} + +const char *strxf_algotype(int type) +{ + int i; + + for (i = 0; ; i++) { + const struct typeent *t = &algo_types[i]; + if (!t->t_name || t->t_type == -1) + break; + + if (t->t_type == type) + return t->t_name; + } + + return NULL; +} + +const char *strxf_mask8(__u8 mask) +{ + static char str[16]; + const int sn = sizeof(mask) * 8 - 1; + __u8 b; + int i = 0; + + for (b = (1 << sn); b > 0; b >>= 1) + str[i++] = ((b & mask) ? '1' : '0'); + str[i] = '\0'; + + return str; +} + +const char *strxf_mask32(__u32 mask) +{ + static char str[16]; + + sprintf(str, "%.8x", mask); + + return str; +} + +const char *strxf_share(__u8 share) +{ + static char str[32]; + + switch (share) { + case XFRM_SHARE_ANY: + strcpy(str, "any"); + break; + case XFRM_SHARE_SESSION: + strcpy(str, "session"); + break; + case XFRM_SHARE_USER: + strcpy(str, "user"); + break; + case XFRM_SHARE_UNIQUE: + strcpy(str, "unique"); + break; + default: + sprintf(str, "%u", share); + break; + } + + return str; +} + +const char *strxf_proto(__u8 proto) +{ + static char buf[32]; + struct protoent *pp; + const char *p; + + pp = getprotobynumber(proto); + if (pp) + p = pp->p_name; + else { + sprintf(buf, "%u", proto); + p = buf; + } + + return p; +} + +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, int force_spi, + FILE *fp, const char *prefix) +{ + char abuf[256]; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "src %s ", rt_addr_n2a(family, sizeof(*saddr), + saddr, abuf, sizeof(abuf))); + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "dst %s", rt_addr_n2a(family, sizeof(id->daddr), + &id->daddr, abuf, sizeof(abuf))); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + + fprintf(fp, "proto %s ", strxf_xfrmproto(id->proto)); + + + if (show_stats > 0 || force_spi || id->spi) { + __u32 spi = ntohl(id->spi); + fprintf(fp, "spi 0x%08x", spi); + if (show_stats > 0) + fprintf(fp, "(%u)", spi); + fprintf(fp, " "); + } + + fprintf(fp, "reqid %u", reqid); + if (show_stats > 0) + fprintf(fp, "(0x%08x)", reqid); + fprintf(fp, " "); + + fprintf(fp, "mode "); + switch (mode) { + case 0: + fprintf(fp, "transport"); + break; + case 1: + fprintf(fp, "tunnel"); + break; + default: + fprintf(fp, "%u", mode); + break; + } + fprintf(fp, "%s", _SL_); +} + +static const char *strxf_limit(__u64 limit) +{ + static char str[32]; + if (limit == XFRM_INF) + strcpy(str, "(INF)"); + else + sprintf(str, "%llu", (unsigned long long) limit); + + return str; +} + +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix) +{ + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "stats:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "replay-window %u ", s->replay_window); + fprintf(fp, "replay %u ", s->replay); + fprintf(fp, "failed %u", s->integrity_failed); + fprintf(fp, "%s", _SL_); +} + +static const char *strxf_time(__u64 time) +{ + static char str[32]; + + if (time == 0) + strcpy(str, "-"); + else { + time_t t; + struct tm *tp; + + /* XXX: treat time in the same manner of kernel's + * net/xfrm/xfrm_{user,state}.c + */ + t = (long)time; + tp = localtime(&t); + + strftime(str, sizeof(str), "%Y-%m-%d %T", tp); + } + + return str; +} + +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix) +{ + if (cfg) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime config:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_byte_limit)); + fprintf(fp, "(bytes), hard "); + fprintf(fp, strxf_limit(cfg->hard_byte_limit)); + fprintf(fp, "(bytes)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "limit: "); + fprintf(fp, "soft "); + fprintf(fp, strxf_limit(cfg->soft_packet_limit)); + fprintf(fp, "(packets), hard "); + fprintf(fp, strxf_limit(cfg->hard_packet_limit)); + fprintf(fp, "(packets)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire add: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", (unsigned long long) cfg->soft_add_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", (unsigned long long) cfg->hard_add_expires_seconds); + fprintf(fp, "(sec)"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "expire use: "); + fprintf(fp, "soft "); + fprintf(fp, "%llu", (unsigned long long) cfg->soft_use_expires_seconds); + fprintf(fp, "(sec), hard "); + fprintf(fp, "%llu", (unsigned long long) cfg->hard_use_expires_seconds); + fprintf(fp, "(sec)"); + fprintf(fp, "%s", _SL_); + } + if (cur) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "lifetime current:"); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "%llu(bytes), ", (unsigned long long) cur->bytes); + fprintf(fp, "%llu(packets)", (unsigned long long) cur->packets); + fprintf(fp, "%s", _SL_); + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, " "); + fprintf(fp, "add %s ", strxf_time(cur->add_time)); + fprintf(fp, "use %s", strxf_time(cur->use_time)); + fprintf(fp, "%s", _SL_); + } +} + +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix) +{ + char abuf[256]; + __u16 f; + + f = sel->family; + if (f == AF_UNSPEC) + f = family; + if (f == AF_UNSPEC) + f = preferred_family; + + if (prefix) + fprintf(fp, prefix); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "src %s/%u ", rt_addr_n2a(f, sizeof(sel->saddr), + &sel->saddr, abuf, sizeof(abuf)), + sel->prefixlen_s); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "dst %s/%u ", rt_addr_n2a(f, sizeof(sel->daddr), + &sel->daddr, abuf, sizeof(abuf)), + sel->prefixlen_d); + + if (sel->proto) + fprintf(fp, "proto %s ", strxf_proto(sel->proto)); + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + default: /* XXX */ + if (sel->sport_mask) + fprintf(fp, "sport %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "dport %u ", ntohs(sel->dport)); + break; + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + /* type/code is stored at sport/dport in selector */ + if (sel->sport_mask) + fprintf(fp, "type %u ", ntohs(sel->sport)); + if (sel->dport_mask) + fprintf(fp, "code %u ", ntohs(sel->dport)); + break; + } + + if (sel->ifindex > 0) { + char buf[IFNAMSIZ]; + + memset(buf, '\0', sizeof(buf)); + if_indextoname(sel->ifindex, buf); + fprintf(fp, "dev %s ", buf); + } + + if (show_stats > 0) + fprintf(fp, "uid %u", sel->user); + + fprintf(fp, "%s", _SL_); +} + +static void xfrm_algo_print(struct xfrm_algo *algo, int type, int len, + FILE *fp, const char *prefix) +{ + int keylen; + int i; + + if (prefix) + fprintf(fp, prefix); + + fprintf(fp, "%s ", strxf_algotype(type)); + + if (len < sizeof(*algo)) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + len -= sizeof(*algo); + + fprintf(fp, "%s ", algo->alg_name); + + keylen = algo->alg_key_len / 8; + if (len < keylen) { + fprintf(fp, "(ERROR truncated)"); + goto fin; + } + + fprintf(fp, "0x"); + for (i = 0; i < keylen; i ++) + fprintf(fp, "%.2x", (unsigned char)algo->alg_key[i]); + + if (show_stats > 0) + fprintf(fp, " (%d bits)", algo->alg_key_len); + + fin: + fprintf(fp, "%s", _SL_); +} + +static void xfrm_tmpl_print(struct xfrm_user_tmpl *tmpls, int len, + __u16 family, FILE *fp, const char *prefix) +{ + int ntmpls = len / sizeof(struct xfrm_user_tmpl); + int i; + + if (ntmpls <= 0) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "(ERROR \"tmpl\" truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + + for (i = 0; i < ntmpls; i++) { + struct xfrm_user_tmpl *tmpl = &tmpls[i]; + + if (prefix) + fprintf(fp, prefix); + + fprintf(fp, "tmpl"); + xfrm_id_info_print(&tmpl->saddr, &tmpl->id, tmpl->mode, + tmpl->reqid, family, 0, fp, prefix); + + if (show_stats > 0 || tmpl->optional) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + switch (tmpl->optional) { + case 0: + if (show_stats > 0) + fprintf(fp, "level required "); + break; + case 1: + fprintf(fp, "level use "); + break; + default: + fprintf(fp, "level %u ", tmpl->optional); + break; + } + + if (show_stats > 0) + fprintf(fp, "share %s ", strxf_share(tmpl->share)); + + fprintf(fp, "%s", _SL_); + } + + if (show_stats > 0) { + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "\t"); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_CRYPT), + strxf_mask32(tmpl->ealgos)); + fprintf(fp, "%s-mask %s ", + strxf_algotype(XFRMA_ALG_AUTH), + strxf_mask32(tmpl->aalgos)); + fprintf(fp, "%s-mask %s", + strxf_algotype(XFRMA_ALG_COMP), + strxf_mask32(tmpl->calgos)); + + fprintf(fp, "%s", _SL_); + } + } +} + +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix) +{ + if (tb[XFRMA_ALG_AUTH]) { + struct rtattr *rta = tb[XFRMA_ALG_AUTH]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_AUTH, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ALG_CRYPT]) { + struct rtattr *rta = tb[XFRMA_ALG_CRYPT]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_CRYPT, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ALG_COMP]) { + struct rtattr *rta = tb[XFRMA_ALG_COMP]; + xfrm_algo_print((struct xfrm_algo *) RTA_DATA(rta), + XFRMA_ALG_COMP, RTA_PAYLOAD(rta), fp, prefix); + } + + if (tb[XFRMA_ENCAP]) { + struct xfrm_encap_tmpl *e; + char abuf[256]; + + if (prefix) + fprintf(fp, prefix); + fprintf(fp, "encap "); + + if (RTA_PAYLOAD(tb[XFRMA_ENCAP]) < sizeof(*e)) { + fprintf(fp, "(ERROR truncated)"); + fprintf(fp, "%s", _SL_); + return; + } + e = (struct xfrm_encap_tmpl *) RTA_DATA(tb[XFRMA_ENCAP]); + + fprintf(fp, "type "); + switch (e->encap_type) { + case 1: + fprintf(fp, "espinudp-nonike "); + break; + case 2: + fprintf(fp, "espinudp "); + break; + default: + fprintf(fp, "%u ", e->encap_type); + break; + } + fprintf(fp, "sport %u ", ntohs(e->encap_sport)); + fprintf(fp, "dport %u ", ntohs(e->encap_dport)); + + memset(abuf, '\0', sizeof(abuf)); + fprintf(fp, "addr %s", + rt_addr_n2a(family, sizeof(e->encap_oa), + &e->encap_oa, abuf, sizeof(abuf))); + fprintf(fp, "%s", _SL_); + } + + if (tb[XFRMA_TMPL]) { + struct rtattr *rta = tb[XFRMA_TMPL]; + xfrm_tmpl_print((struct xfrm_user_tmpl *) RTA_DATA(rta), + RTA_PAYLOAD(rta), family, fp, prefix); + } +} + +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"src\" address family is AF_UNSPEC", *argv); + if (family) + *family = src.family; + + memcpy(saddr, &src.data, sizeof(*saddr)); + + filter.id_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"dst\" address family is AF_UNSPEC", *argv); + if (family) + *family = dst.family; + + memcpy(&id->daddr, &dst.data, sizeof(id->daddr)); + + filter.id_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "proto") == 0) { + int ret; + + NEXT_ARG(); + + ret = xfrm_xfrmproto_getbyname(*argv); + if (ret < 0) + invarg("\"XFRM_PROTO\" is invalid", *argv); + + id->proto = (__u8)ret; + + filter.id_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "spi") == 0) { + __u32 spi; + + NEXT_ARG(); + if (get_u32(&spi, *argv, 0)) + invarg("\"SPI\" is invalid", *argv); + + spi = htonl(spi); + id->spi = spi; + + filter.id_spi_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"src\" and \"dst\"", *argv); + + if (loose == 0 && id->proto == 0) + missarg("XFRM_PROTO"); + if (argc == *argcp) + missarg("ID"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (matches(*argv, "transport") == 0) + *mode = 0; + else if (matches(*argv, "tunnel") == 0) + *mode = 1; + else + invarg("\"MODE\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "espinudp-nonike") == 0) + *type = 1; + else if (strcmp(*argv, "espinudp") == 0) + *type = 2; + else + invarg("\"ENCAP-TYPE\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +/* NOTE: reqid is used by host-byte order */ +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (get_u32(reqid, *argv, 0)) + invarg("\"REQID\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_selector_upspec_parse(struct xfrm_selector *sel, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *sportp = NULL; + char *dportp = NULL; + char *typep = NULL; + char *codep = NULL; + + while (1) { + if (strcmp(*argv, "proto") == 0) { + __u8 upspec; + + NEXT_ARG(); + + if (strcmp(*argv, "any") == 0) + upspec = 0; + else { + struct protoent *pp; + pp = getprotobyname(*argv); + if (pp) + upspec = pp->p_proto; + else { + if (get_u8(&upspec, *argv, 0)) + invarg("\"PROTO\" is invalid", *argv); + } + } + sel->proto = upspec; + + filter.upspec_proto_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "sport") == 0) { + sportp = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->sport = htons(sel->sport); + if (sel->sport) + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "dport") == 0) { + dportp = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0)) + invarg("\"PORT\" is invalid", *argv); + sel->dport = htons(sel->dport); + if (sel->dport) + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "type") == 0) { + typep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->sport, *argv, 0) || + (sel->sport & ~((__u16)0xff))) + invarg("\"type\" value is invalid", *argv); + sel->sport = htons(sel->sport); + sel->sport_mask = ~((__u16)0); + + filter.upspec_sport_mask = XFRM_FILTER_MASK_FULL; + + + } else if (strcmp(*argv, "code") == 0) { + codep = *argv; + + NEXT_ARG(); + + if (get_u16(&sel->dport, *argv, 0) || + (sel->dport & ~((__u16)0xff))) + invarg("\"code\" value is invalid", *argv); + sel->dport = htons(sel->dport); + sel->dport_mask = ~((__u16)0); + + filter.upspec_dport_mask = XFRM_FILTER_MASK_FULL; + + } else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + if (argc == *argcp) + missarg("UPSPEC"); + if (sportp || dportp) { + switch (sel->proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + break; + default: + fprintf(stderr, "\"sport\" and \"dport\" are invalid with proto=%s\n", strxf_proto(sel->proto)); + exit(1); + } + } + if (typep || codep) { + switch (sel->proto) { + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + break; + default: + fprintf(stderr, "\"type\" and \"code\" are invalid with proto=%s\n", strxf_proto(sel->proto)); + exit(1); + } + } + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + inet_prefix dst; + inet_prefix src; + char *upspecp = NULL; + + memset(&dst, 0, sizeof(dst)); + memset(&src, 0, sizeof(src)); + + while (1) { + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + + get_prefix(&src, *argv, preferred_family); + if (src.family == AF_UNSPEC) + invarg("\"src\" address family is AF_UNSPEC", *argv); + sel->family = src.family; + + memcpy(&sel->saddr, &src.data, sizeof(sel->saddr)); + sel->prefixlen_s = src.bitlen; + + filter.sel_src_mask = src.bitlen; + + } else if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + + get_prefix(&dst, *argv, preferred_family); + if (dst.family == AF_UNSPEC) + invarg("\"dst\" address family is AF_UNSPEC", *argv); + sel->family = dst.family; + + memcpy(&sel->daddr, &dst.data, sizeof(sel->daddr)); + sel->prefixlen_d = dst.bitlen; + + filter.sel_dst_mask = dst.bitlen; + + } else if (strcmp(*argv, "dev") == 0) { + int ifindex; + + NEXT_ARG(); + + if (strcmp(*argv, "none") == 0) + ifindex = 0; + else { + ifindex = if_nametoindex(*argv); + if (ifindex <= 0) + invarg("\"DEV\" is invalid", *argv); + } + sel->ifindex = ifindex; + + filter.sel_dev_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (upspecp) { + PREV_ARG(); /* back track */ + break; + } else { + upspecp = *argv; + xfrm_selector_upspec_parse(sel, &argc, &argv); + } + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + + if (src.family && dst.family && (src.family != dst.family)) + invarg("the same address family is required between \"src\" and \"dst\"", *argv); + + if (argc == *argcp) + missarg("SELECTOR"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int ret; + + if (strcmp(*argv, "time-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_add_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "time-use-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_use_expires_seconds, *argv, 0); + if (ret) + invarg("\"time-use-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "byte-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_byte_limit, *argv, 0); + if (ret) + invarg("\"byte-hard\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-soft") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->soft_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-soft\" value is invalid", *argv); + } else if (strcmp(*argv, "packet-hard") == 0) { + NEXT_ARG(); + ret = get_u64(&lft->hard_packet_limit, *argv, 0); + if (ret) + invarg("\"packet-hard\" value is invalid", *argv); + } else + invarg("\"LIMIT\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +int do_xfrm(int argc, char **argv) +{ + memset(&filter, 0, sizeof(filter)); + + if (argc < 1) + usage(); + + if (matches(*argv, "state") == 0 || + matches(*argv, "sa") == 0) { + return do_xfrm_state(argc-1, argv+1); + } else if (matches(*argv, "policy") == 0) + return do_xfrm_policy(argc-1, argv+1); + else if (matches(*argv, "help") == 0) { + usage(); + fprintf(stderr, "xfrm Object \"%s\" is unknown.\n", *argv); + exit(-1); + } + usage(); +} diff --git a/ip/ipxfrm.o b/ip/ipxfrm.o new file mode 100644 index 0000000000000000000000000000000000000000..5c4380f4e0c23606b1d71a3c72160f3f17e05132 GIT binary patch literal 26728 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 Vm%nT1RdD&cgFXe{Y6$bS{{m`rNrwOc literal 0 HcmV?d00001 diff --git a/ip/routef b/ip/routef new file mode 100755 index 0000000..db43b5d --- /dev/null +++ b/ip/routef @@ -0,0 +1,3 @@ +#! /bin/sh + +exec ip -4 ro flush scope global type unicast diff --git a/ip/routel b/ip/routel new file mode 100755 index 0000000..8d1d352 --- /dev/null +++ b/ip/routel @@ -0,0 +1,60 @@ +#!/bin/sh +#$Id$ + +# +# Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18 +# Donated to the public domain. +# +# This script transforms the output of "ip" into more readable text. +# "ip" is the Linux-advanced-routing configuration tool part of the +# iproute package. +# + +test "X-h" = "X$1" && echo "Usage: $0 [tablenr [raw ip args...]]" && exit 64 + +test -z "$*" && set 0 + +ip route list table "$@" | + while read network rest + do set xx $rest + shift + proto="" + via="" + dev="" + scope="" + src="" + table="" + case $network in + broadcast|local|unreachable) via=$network + network=$1 + shift + ;; + esac + while test $# != 0 + do + key=$1 + val=$2 + eval "$key=$val" + shift 2 + done + echo "$network $via $src $proto $scope $dev $table" + done | awk -F ' ' ' +BEGIN { + format="%15s%-3s %15s %15s %8s %8s%7s %s\n"; + printf(format,"target","","gateway","source","proto","scope","dev","tbl"); + } + { network=$1; + mask=""; + if(match(network,"/")) + { mask=" "substr(network,RSTART+1); + network=substr(network,0,RSTART); + } + via=$2; + src=$3; + proto=$4; + scope=$5; + dev=$6; + table=$7; + printf(format,network,mask,via,src,proto,scope,dev,table); + } +' diff --git a/ip/rtm_map.c b/ip/rtm_map.c new file mode 100644 index 0000000..21e818b --- /dev/null +++ b/ip/rtm_map.c @@ -0,0 +1,116 @@ +/* + * rtm_map.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "rt_names.h" +#include "utils.h" + +char *rtnl_rtntype_n2a(int id, char *buf, int len) +{ + switch (id) { + case RTN_UNSPEC: + return "none"; + case RTN_UNICAST: + return "unicast"; + case RTN_LOCAL: + return "local"; + case RTN_BROADCAST: + return "broadcast"; + case RTN_ANYCAST: + return "anycast"; + case RTN_MULTICAST: + return "multicast"; + case RTN_BLACKHOLE: + return "blackhole"; + case RTN_UNREACHABLE: + return "unreachable"; + case RTN_PROHIBIT: + return "prohibit"; + case RTN_THROW: + return "throw"; + case RTN_NAT: + return "nat"; + case RTN_XRESOLVE: + return "xresolve"; + default: + snprintf(buf, len, "%d", id); + return buf; + } +} + + +int rtnl_rtntype_a2n(int *id, char *arg) +{ + char *end; + unsigned long res; + + if (strcmp(arg, "local") == 0) + res = RTN_LOCAL; + else if (strcmp(arg, "nat") == 0) + res = RTN_NAT; + else if (matches(arg, "broadcast") == 0 || + strcmp(arg, "brd") == 0) + res = RTN_BROADCAST; + else if (matches(arg, "anycast") == 0) + res = RTN_ANYCAST; + else if (matches(arg, "multicast") == 0) + res = RTN_MULTICAST; + else if (matches(arg, "prohibit") == 0) + res = RTN_PROHIBIT; + else if (matches(arg, "unreachable") == 0) + res = RTN_UNREACHABLE; + else if (matches(arg, "blackhole") == 0) + res = RTN_BLACKHOLE; + else if (matches(arg, "xresolve") == 0) + res = RTN_XRESOLVE; + else if (matches(arg, "unicast") == 0) + res = RTN_UNICAST; + else if (strcmp(arg, "throw") == 0) + res = RTN_THROW; + else { + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + } + *id = res; + return 0; +} + +int get_rt_realms(__u32 *realms, char *arg) +{ + __u32 realm = 0; + char *p = strchr(arg, '/'); + + *realms = 0; + if (p) { + *p = 0; + if (rtnl_rtrealm_a2n(realms, arg)) { + *p = '/'; + return -1; + } + *realms <<= 16; + *p = '/'; + arg = p+1; + } + if (*arg && rtnl_rtrealm_a2n(&realm, arg)) + return -1; + *realms |= realm; + return 0; +} diff --git a/ip/rtm_map.o b/ip/rtm_map.o new file mode 100644 index 0000000000000000000000000000000000000000..2daff0e7ecedf1165786bb6c981842cac223edb1 GIT binary patch literal 4088 zcmcImU2GIp6u#SCp@p(jh%r@3n2<qBlwr4!TEvj8P#KzqAlPUGTxNG?-Qw(Qot>3J zOsLr{VNysM6QkjYm}q=5_=1!~Tnd!v6Tue}6NoQbf)6M$fuy>gGjq=}Ty_&5@FX+m zobP=1=bStD-Z?Uue61}MVk9B9i9PTLWi0l@13TfF2_~^lR;e%Et+{s(cDVQI%4G~_ znLpk8=RC{38L!Rx*7ZFX_nbxZ@v5oe=>^cK=Keju`+I++yISS<_PbwBw}C5tIwO5t z>z{F+m(GMMb5{8&>CEO(Wlk?I0i&%l=aeI0gePj#i-2BU>TYN8npU~gzGvUXTy82i zb>8pvrkBP<O{%Vh>Uf={rK$`zVo}fnaXJDGozo(URdh~@Xgi`+5nTwVR6S6y*To2? zELC?li7fUaeN7^`s8V$~MCQL)z)N)l%G7~4FOyWA|EFG${Sd#VxxZ?zpS0#)&?X~_ zHc@j{XdA9O&y4po>601lqx*HQX|3bxS0Sl711--&@AX^Xfi)e5*sZU@@(XTD6F>P5 z6g?|V%=%8)Bj>Qv{L$RK%ADq!${qNsl5SSH2g}*B&7HrpMd^e-+4;5bYRqk-rn#2~ zu0#|JIV)x{bR+5h#+R!+q@CCo8n~b+U9*F1?f6DkmM5X3fsTj*Nlxxny0l4C>2|Ls zC$owS3*U25JMp2?3BB+x&S6f@kGHRR2KI9M3XHWX%f@V@R5U*@m{qh4R<=emddXpC zF{7I-Z5Q>d=j+xXPZ!Fj<1^Bxo_Q}{G_j3s=$X8p27A;l=10;a4s-H$@gTEw2UGgr z<w-t5h=s<wIv2K2;dcVEoIjoeuOr;p678zs&ccGf!ZB2rc)NjU*U`42Xm=%?jLLu` zFAERJ7u*;+8m>f=(b#e93}x%)%`eM6TXzhvme;B4)n2(b7F!>W#b1(pb{Sb&(;XjQ z)2HS#>sB+SmJSshJq_BieVr%EHcVZ0j4_9)S>4f@nl6=?Y8TN#dR&czizFZ8j*(C0 zY`tJG)harM3UgUga$v2Pnkg0vhV?jBM21wcRJetMlHkr?R1fZV1xa8(3r|GA(Cx;Z z4>E=u4`c~EI1U~yDBZqd&w)mq(>TglzX<S%IM$1RW1k}ZzqXG<TO7X^o}f_s;J1Qt z<f4!s<hD>Ic8X7EKavAN`4f}?L(B<dQ&D^~Ci8)?ez-=|p6a&?Fr<j%Q>Vuu78Ghv z*mf|QjelD585$?z_}G@()A)M<qgrsPr;au2v<cU2f?)^L9@i%*;#f-|D6T#7z55Py z8J&QL?VHvP_;+}sr+EWh4!n}Z{PDVKHvTCjgB<i#<1LQg2nYu9BJ2;m(jJj;(~9Ff zn#~09=8F+#c>O;r;YUsbpA&9m|8SeISDA+~^umlE0)Y@B%L4PKa~#ii@od3nt2log z9S}O#`@n9--_!zs9dI0XC*LS^?x$Mtb1iUYJB3t1A5}A8S!N1e&Y@8wW%cT;WR2P* zmXmupqW4;?pgWnoQDUhSUJ1prnF5|+TW~YKYdFwJ3NBu=P+}0y<ZbG}15?-m<Hvzl z(coE4`w&03;A*Y}9|hCJn$P2J1IQW#L5NRZ%z7Vg;NHItU>gM-_Z!NqzH0EF=KeMT zU%~yI0uC3ZCvOTk?e}*CoaSK&_#pT396_PHaJ^AVzG~oabN{e_@8|w;0jIov5pc@u z7RRy9ct%p47x9fw^Pzl~aa`of0-x&8(}Le8@G0L-E%-@+Pj!2v1wSY7ss2_A{<y%W z@9J0!{z-wql-Kd|7W{7oKCR=803Y`V)&DRS7z7ob6NG;jz;R5%e{O*T#S<I`DfFK} z_W<D=d|Km7B0Rxy+NXqX<+#i>;XAnBBjCf_j|n*5zbO5_@=jKqXNLO;0YAWfO~Bvh fKJ8PQ<zw68JB0UlwrgvV?$-b3ZTObRPc;n=wix literal 0 HcmV?d00001 diff --git a/ip/rtmon b/ip/rtmon new file mode 100755 index 0000000000000000000000000000000000000000..179849b07b87bd6bfd398951b95683b6cc0e1b22 GIT binary patch literal 30290 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 YA2=u?HMJRMHPh$h>TB^V9@PJT05RD4a{vGU literal 0 HcmV?d00001 diff --git a/ip/rtmon.c b/ip/rtmon.c new file mode 100644 index 0000000..5ce7731 --- /dev/null +++ b/ip/rtmon.c @@ -0,0 +1,178 @@ +/* + * rtmon.c RTnetlink listener. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <string.h> + +#include "SNAPSHOT.h" + +#include "utils.h" +#include "libnetlink.h" + +int resolve_hosts = 0; +static int init_phase = 1; + +static void write_stamp(FILE *fp) +{ + char buf[128]; + struct nlmsghdr *n1 = (void*)buf; + struct timeval tv; + + n1->nlmsg_type = 15; + n1->nlmsg_flags = 0; + n1->nlmsg_seq = 0; + n1->nlmsg_pid = 0; + n1->nlmsg_len = NLMSG_LENGTH(4*2); + gettimeofday(&tv, NULL); + ((__u32*)NLMSG_DATA(n1))[0] = tv.tv_sec; + ((__u32*)NLMSG_DATA(n1))[1] = tv.tv_usec; + fwrite((void*)n1, 1, NLMSG_ALIGN(n1->nlmsg_len), fp); +} + +static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + if (!init_phase) + write_stamp(fp); + fwrite((void*)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp); + fflush(fp); + return 0; +} + +void usage(void) +{ + fprintf(stderr, "Usage: rtmon file FILE [ all | LISTofOBJECTS]\n"); + fprintf(stderr, "LISTofOBJECTS := [ link ] [ address ] [ route ]\n"); + exit(-1); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + struct rtnl_handle rth; + int family = AF_UNSPEC; + unsigned groups = ~0U; + int llink = 0; + int laddr = 0; + int lroute = 0; + char *file = NULL; + + while (argc > 1) { + if (matches(argv[1], "-family") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + if (strcmp(argv[1], "inet") == 0) + family = AF_INET; + else if (strcmp(argv[1], "inet6") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "link") == 0) + family = AF_INET6; + else if (strcmp(argv[1], "help") == 0) + usage(); + else { + fprintf(stderr, "Protocol ID \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + } else if (strcmp(argv[1], "-4") == 0) { + family = AF_INET; + } else if (strcmp(argv[1], "-6") == 0) { + family = AF_INET6; + } else if (strcmp(argv[1], "-0") == 0) { + family = AF_PACKET; + } else if (matches(argv[1], "-Version") == 0) { + printf("rtmon utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + } else if (matches(argv[1], "file") == 0) { + argc--; + argv++; + if (argc <= 1) + usage(); + file = argv[1]; + } else if (matches(argv[1], "link") == 0) { + llink=1; + groups = 0; + } else if (matches(argv[1], "address") == 0) { + laddr=1; + groups = 0; + } else if (matches(argv[1], "route") == 0) { + lroute=1; + groups = 0; + } else if (strcmp(argv[1], "all") == 0) { + groups = ~0U; + } else if (matches(argv[1], "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (file == NULL) { + fprintf(stderr, "Not enough information: argument \"file\" is required\n"); + exit(-1); + } + if (llink) + groups |= RTMGRP_LINK; + if (laddr) { + if (!family || family == AF_INET) + groups |= RTMGRP_IPV4_IFADDR; + if (!family || family == AF_INET6) + groups |= RTMGRP_IPV6_IFADDR; + } + if (lroute) { + if (!family || family == AF_INET) + groups |= RTMGRP_IPV4_ROUTE; + if (!family || family == AF_INET6) + groups |= RTMGRP_IPV6_ROUTE; + } + + fp = fopen(file, "w"); + if (fp == NULL) { + perror("Cannot fopen"); + exit(-1); + } + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + write_stamp(fp); + + if (rtnl_dump_filter(&rth, dump_msg, fp, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + init_phase = 0; + + if (rtnl_listen(&rth, dump_msg, (void*)fp) < 0) + exit(2); + + exit(0); +} diff --git a/ip/rtmon.o b/ip/rtmon.o new file mode 100644 index 0000000000000000000000000000000000000000..7729575b8666b11e1fca618142aa65d9339d8d3a GIT binary patch literal 5720 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 literal 0 HcmV?d00001 diff --git a/ip/rtpr b/ip/rtpr new file mode 100755 index 0000000..c3629fd --- /dev/null +++ b/ip/rtpr @@ -0,0 +1,4 @@ +#! /bin/bash + +exec tr "[\\\\]" "[ +]" diff --git a/ip/xfrm.h b/ip/xfrm.h new file mode 100644 index 0000000..fa551b1 --- /dev/null +++ b/ip/xfrm.h @@ -0,0 +1,119 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#ifndef __XFRM_H__ +#define __XFRM_H__ 1 + +#include <stdio.h> +#include <sys/socket.h> +#include <linux/xfrm.h> + +#ifndef IPPROTO_SCTP +# define IPPROTO_SCTP 132 +#endif + +#define XFRMS_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info)))) +#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info)) + +#define XFRMP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info)))) +#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info)) + +#define XFRM_FLAG_PRINT(fp, flags, f, s) \ + do { \ + if (flags & f) { \ + flags &= ~f; \ + fprintf(fp, s "%s", (flags ? " " : "")); \ + } \ + } while(0) + +struct xfrm_buffer { + char *buf; + int size; + int offset; + + int nlmsg_count; + struct rtnl_handle *rth; +}; + +struct xfrm_filter { + int use; + + struct xfrm_usersa_info xsinfo; + __u8 id_src_mask; + __u8 id_dst_mask; + __u8 id_proto_mask; + __u32 id_spi_mask; + __u8 mode_mask; + __u32 reqid_mask; + __u8 state_flags_mask; + + struct xfrm_userpolicy_info xpinfo; + __u8 dir_mask; + __u8 sel_src_mask; + __u8 sel_dst_mask; + __u32 sel_dev_mask; + __u8 upspec_proto_mask; + __u16 upspec_sport_mask; + __u16 upspec_dport_mask; + __u32 index_mask; + __u8 action_mask; + __u32 priority_mask; +}; +#define XFRM_FILTER_MASK_FULL (~0) + +extern struct xfrm_filter filter; + +int do_xfrm_state(int argc, char **argv); +int do_xfrm_policy(int argc, char **argv); + +int xfrm_addr_match(xfrm_address_t *x1, xfrm_address_t *x2, int bits); +int xfrm_xfrmproto_getbyname(char *name); +int xfrm_algotype_getbyname(char *name); +const char *strxf_xfrmproto(__u8 proto); +const char *strxf_algotype(int type); +const char *strxf_mask8(__u8 mask); +const char *strxf_mask32(__u32 mask); +const char *strxf_share(__u8 share); +const char *strxf_proto(__u8 proto); +void xfrm_id_info_print(xfrm_address_t *saddr, struct xfrm_id *id, + __u8 mode, __u32 reqid, __u16 family, int force_spi, + FILE *fp, const char *prefix); +void xfrm_stats_print(struct xfrm_stats *s, FILE *fp, const char *prefix); +void xfrm_lifetime_print(struct xfrm_lifetime_cfg *cfg, + struct xfrm_lifetime_cur *cur, + FILE *fp, const char *prefix); +void xfrm_selector_print(struct xfrm_selector *sel, __u16 family, + FILE *fp, const char *prefix); +void xfrm_xfrma_print(struct rtattr *tb[], __u16 family, + FILE *fp, const char *prefix); +int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family, + int loose, int *argcp, char ***argvp); +int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp); +int xfrm_encap_type_parse(__u16 *type, int *argcp, char ***argvp); +int xfrm_reqid_parse(__u32 *reqid, int *argcp, char ***argvp); +int xfrm_selector_parse(struct xfrm_selector *sel, int *argcp, char ***argvp); +int xfrm_lifetime_cfg_parse(struct xfrm_lifetime_cfg *lft, + int *argcp, char ***argvp); + +#endif diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c new file mode 100644 index 0000000..c1331a4 --- /dev/null +++ b/ip/xfrm_policy.c @@ -0,0 +1,736 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/netlink.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_userpolicy_info + * rtattr + * data = struct xfrm_user_tmpl[] + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_TMPLS_BUF_SIZE 1024 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm policy { add | update } dir DIR SELECTOR [ index INDEX ] \n"); + fprintf(stderr, " [ action ACTION ] [ priority PRIORITY ] [ LIMIT-LIST ] [ TMPL-LIST ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { delete | get } dir DIR [ SELECTOR | index INDEX ]\n"); + fprintf(stderr, "Usage: ip xfrm policy { flush | list } [ dir DIR ] [ SELECTOR ]\n"); + fprintf(stderr, " [ index INDEX ] [ action ACTION ] [ priority PRIORITY ]\n"); + fprintf(stderr, "DIR := [ in | out | fwd ]\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n"); + fprintf(stderr, " [ type NUMBER ] [ code NUMBER ] ]\n"); + + //fprintf(stderr, "DEV - device name(default=none)\n"); + + fprintf(stderr, "ACTION := [ allow | block ](default=allow)\n"); + + //fprintf(stderr, "PRIORITY - priority value(default=0)\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"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] NUMBER ]\n"); + + fprintf(stderr, "TMPL-LIST := [ TMPL-LIST ] | [ tmpl TMPL ]\n"); + fprintf(stderr, "TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]\n"); + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + + //fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n"); + fprintf(stderr, "XFRM_PROTO := [ "); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); + fprintf(stderr, "%s", strxf_xfrmproto(IPPROTO_COMP)); + fprintf(stderr, " ]\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + fprintf(stderr, "LEVEL := [ required | use ](default=required)\n"); + + exit(-1); +} + +static int xfrm_policy_dir_parse(__u8 *dir, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + + if (strcmp(*argv, "in") == 0) + *dir = XFRM_POLICY_IN; + else if (strcmp(*argv, "out") == 0) + *dir = XFRM_POLICY_OUT; + else if (strcmp(*argv, "fwd") == 0) + *dir = XFRM_POLICY_FWD; + else + invarg("\"DIR\" is invalid", *argv); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_tmpl_parse(struct xfrm_user_tmpl *tmpl, + int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + char *idp = NULL; + + while (1) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&tmpl->mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&tmpl->reqid, &argc, &argv); + } else if (strcmp(*argv, "level") == 0) { + NEXT_ARG(); + + if (strcmp(*argv, "required") == 0) + tmpl->optional = 0; + else if (strcmp(*argv, "use") == 0) + tmpl->optional = 1; + else + invarg("\"LEVEL\" is invalid\n", *argv); + + } else { + if (idp) { + PREV_ARG(); /* back track */ + break; + } + idp = *argv; + xfrm_id_parse(&tmpl->saddr, &tmpl->id, &tmpl->family, + 0, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = tmpl->family; + } + + if (!NEXT_ARG_OK()) + break; + + NEXT_ARG(); + } + if (argc == *argcp) + missarg("TMPL"); + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_policy_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_info xpinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *dirp = NULL; + char *selp = NULL; + char tmpls_buf[XFRM_TMPLS_BUF_SIZE]; + int tmpls_len = 0; + + memset(&req, 0, sizeof(req)); + memset(&tmpls_buf, 0, sizeof(tmpls_buf)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xpinfo.sel.family = preferred_family; + + req.xpinfo.lft.soft_byte_limit = XFRM_INF; + req.xpinfo.lft.hard_byte_limit = XFRM_INF; + req.xpinfo.lft.soft_packet_limit = XFRM_INF; + req.xpinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + req.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + req.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"action\" value is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&req.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xpinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "tmpl") == 0) { + struct xfrm_user_tmpl *tmpl; + + if (tmpls_len + sizeof(*tmpl) > sizeof(tmpls_buf)) { + fprintf(stderr, "Too many tmpls: buffer overflow\n"); + exit(1); + } + tmpl = (struct xfrm_user_tmpl *)((char *)tmpls_buf + tmpls_len); + + tmpl->family = preferred_family; + tmpl->aalgos = (~(__u32)0); + tmpl->ealgos = (~(__u32)0); + tmpl->calgos = (~(__u32)0); + + NEXT_ARG(); + xfrm_tmpl_parse(tmpl, &argc, &argv); + + tmpls_len += sizeof(*tmpl); + } else { + if (selp) + duparg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpinfo.sel.family; + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + + if (tmpls_len > 0) { + addattr_l(&req.n, sizeof(req), XFRMA_TMPL, + (void *)tmpls_buf, tmpls_len); + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpinfo.sel.family == AF_UNSPEC) + req.xpinfo.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_filter_match(struct xfrm_userpolicy_info *xpinfo) +{ + if (!filter.use) + return 1; + + if ((xpinfo->dir^filter.xpinfo.dir)&filter.dir_mask) + return 0; + + if (filter.sel_src_mask) { + if (xfrm_addr_match(&xpinfo->sel.saddr, &filter.xpinfo.sel.saddr, + filter.sel_src_mask)) + return 0; + } + + if (filter.sel_dst_mask) { + if (xfrm_addr_match(&xpinfo->sel.daddr, &filter.xpinfo.sel.daddr, + filter.sel_dst_mask)) + return 0; + } + + if ((xpinfo->sel.ifindex^filter.xpinfo.sel.ifindex)&filter.sel_dev_mask) + return 0; + + if ((xpinfo->sel.proto^filter.xpinfo.sel.proto)&filter.upspec_proto_mask) + return 0; + + if (filter.upspec_sport_mask) { + if ((xpinfo->sel.sport^filter.xpinfo.sel.sport)&filter.upspec_sport_mask) + return 0; + } + + if (filter.upspec_dport_mask) { + if ((xpinfo->sel.dport^filter.xpinfo.sel.dport)&filter.upspec_dport_mask) + return 0; + } + + if ((xpinfo->index^filter.xpinfo.index)&filter.index_mask) + return 0; + + if ((xpinfo->action^filter.xpinfo.action)&filter.action_mask) + return 0; + + if ((xpinfo->priority^filter.xpinfo.priority)&filter.priority_mask) + return 0; + + return 1; +} + +static int xfrm_policy_print(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRMA_MAX+1]; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY && + n->nlmsg_type != XFRM_MSG_DELPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + parse_rtattr(tb, XFRMA_MAX, XFRMP_RTA(xpinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELPOLICY) + fprintf(fp, "Deleted "); + + xfrm_selector_print(&xpinfo->sel, preferred_family, fp, NULL); + + fprintf(fp, "\t"); + fprintf(fp, "dir "); + switch (xpinfo->dir) { + case XFRM_POLICY_IN: + fprintf(fp, "in"); + break; + case XFRM_POLICY_OUT: + fprintf(fp, "out"); + break; + case XFRM_POLICY_FWD: + fprintf(fp, "fwd"); + break; + default: + fprintf(fp, "%u", xpinfo->dir); + break; + } + fprintf(fp, " "); + + switch (xpinfo->action) { + case XFRM_POLICY_ALLOW: + if (show_stats > 0) + fprintf(fp, "action allow "); + break; + case XFRM_POLICY_BLOCK: + fprintf(fp, "action block "); + break; + default: + fprintf(fp, "action %u ", xpinfo->action); + break; + } + + if (show_stats) + fprintf(fp, "index %u ", xpinfo->index); + fprintf(fp, "priority %u ", xpinfo->priority); + if (show_stats > 0) { + fprintf(fp, "share %s ", strxf_share(xpinfo->share)); + fprintf(fp, "flag 0x%s", strxf_mask8(xpinfo->flags)); + } + fprintf(fp, "%s", _SL_); + + if (show_stats > 0) + xfrm_lifetime_print(&xpinfo->lft, &xpinfo->curlft, fp, "\t"); + + xfrm_xfrma_print(tb, xpinfo->sel.family, fp, "\t"); + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_policy_get_or_delete(int argc, char **argv, int delete, + void *res_nlbuf) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_userpolicy_id xpid; + } req; + char *dirp = NULL; + char *selp = NULL; + char *indexp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELPOLICY : XFRM_MSG_GETPOLICY; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + if (dirp) + duparg("dir", *argv); + dirp = *argv; + + NEXT_ARG(); + xfrm_policy_dir_parse(&req.xpid.dir, &argc, &argv); + + } else if (strcmp(*argv, "index") == 0) { + if (indexp) + duparg("index", *argv); + indexp = *argv; + + NEXT_ARG(); + if (get_u32(&req.xpid.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&req.xpid.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xpid.sel.family; + + } + + argc--; argv++; + } + + if (!dirp) { + fprintf(stderr, "Not enough information: \"DIR\" is required.\n"); + exit(1); + } + if (!selp && !indexp) { + fprintf(stderr, "Not enough information: either \"SELECTOR\" or \"INDEX\" is required.\n"); + exit(1); + } + if (selp && indexp) + duparg2("SELECTOR", "INDEX"); + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xpid.sel.family == AF_UNSPEC) + req.xpid.sel.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, res_nlbuf, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_policy_delete(int argc, char **argv) +{ + return xfrm_policy_get_or_delete(argc, argv, 1, NULL); +} + +static int xfrm_policy_get(int argc, char **argv) +{ + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + xfrm_policy_get_or_delete(argc, argv, 0, n); + + if (xfrm_policy_print(NULL, n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + + return 0; +} + +/* + * With an existing policy of nlmsg, make new nlmsg for deleting the policy + * and store it to buffer. + */ +static int xfrm_policy_keep(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_userpolicy_id *xpid; + + if (n->nlmsg_type != XFRM_MSG_NEWPOLICY) { + fprintf(stderr, "Not a policy: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xpinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_policy_filter_match(xpinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xpid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELPOLICY; + new_n->nlmsg_seq = ++rth->seq; + + xpid = NLMSG_DATA(new_n); + memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel)); + xpid->dir = xpinfo->dir; + xpid->index = xpinfo->index; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_policy_list_or_flush(int argc, char **argv, int flush) +{ + char *selp = NULL; + struct rtnl_handle rth; + + if (argc > 0) + filter.use = 1; + filter.xpinfo.sel.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "dir") == 0) { + NEXT_ARG(); + xfrm_policy_dir_parse(&filter.xpinfo.dir, &argc, &argv); + + filter.dir_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.index, *argv, 0)) + invarg("\"INDEX\" is invalid", *argv); + + filter.index_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "allow") == 0) + filter.xpinfo.action = XFRM_POLICY_ALLOW; + else if (strcmp(*argv, "block") == 0) + filter.xpinfo.action = XFRM_POLICY_BLOCK; + else + invarg("\"ACTION\" is invalid\n", *argv); + + filter.action_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + if (get_u32(&filter.xpinfo.priority, *argv, 0)) + invarg("\"PRIORITY\" is invalid", *argv); + + filter.priority_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (selp) + invarg("unknown", *argv); + selp = *argv; + + xfrm_selector_parse(&filter.xpinfo.sel, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xpinfo.sel.family; + + } + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETPOLICY) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_policy_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int xfrm_policy_flush_all(void) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(0); /* nlmsg data is nothing */ + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_FLUSHPOLICY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush all\n"); + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_policy(int argc, char **argv) +{ + if (argc < 1) + return xfrm_policy_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_policy_modify(XFRM_MSG_UPDPOLICY, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_policy_delete(argc-1, argv+1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_policy_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_policy_get(argc-1, argv+1); + if (matches(*argv, "flush") == 0) { + if (argc-1 < 1) + return xfrm_policy_flush_all(); + else + return xfrm_policy_list_or_flush(argc-1, argv+1, 1); + } + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv); + exit(-1); +} diff --git a/ip/xfrm_policy.o b/ip/xfrm_policy.o new file mode 100644 index 0000000000000000000000000000000000000000..34cbd9ee383c0d929df25bee07e3725720e946c7 GIT binary patch literal 21080 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�bePOGVz;i>UlY*sa3TMo3OY<%iP)zQ}J=NU$I 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 BTxkFR literal 0 HcmV?d00001 diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c new file mode 100644 index 0000000..b5b6214 --- /dev/null +++ b/ip/xfrm_state.c @@ -0,0 +1,762 @@ +/* $USAGI: $ */ + +/* + * Copyright (C)2004 USAGI/WIDE Project + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on iproute.c + */ +/* + * Authors: + * Masahide NAKAMURA @USAGI + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <linux/xfrm.h> +#include "utils.h" +#include "xfrm.h" +#include "ip_common.h" + +//#define NLMSG_FLUSH_BUF_SIZE (4096-512) +#define NLMSG_FLUSH_BUF_SIZE 8192 + +/* + * Receiving buffer defines: + * nlmsg + * data = struct xfrm_usersa_info + * rtattr + * rtattr + * ... (max count of rtattr is XFRM_MAX+1 + * + * each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t + */ +#define NLMSG_BUF_SIZE 4096 +#define RTA_BUF_SIZE 2048 +#define XFRM_ALGO_KEY_BUF_SIZE 512 + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]\n"); + fprintf(stderr, " [ reqid REQID ] [ replay-window SIZE ] [ flag FLAG-LIST ]\n"); + fprintf(stderr, " [ encap ENCAP ] [ sel SELECTOR ] [ LIMIT-LIST ]\n"); + fprintf(stderr, "Usage: ip xfrm state { delete | get } ID\n"); + fprintf(stderr, "Usage: ip xfrm state { flush | list } [ ID ] [ mode MODE ] [ reqid REQID ]\n"); + fprintf(stderr, " [ flag FLAG_LIST ]\n"); + + fprintf(stderr, "ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]\n"); + //fprintf(stderr, "XFRM_PROTO := [ esp | ah | comp ]\n"); + fprintf(stderr, "XFRM_PROTO := [ "); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_ESP)); + fprintf(stderr, "%s | ", strxf_xfrmproto(IPPROTO_AH)); + fprintf(stderr, "%s ", strxf_xfrmproto(IPPROTO_COMP)); + fprintf(stderr, "]\n"); + + //fprintf(stderr, "SPI - security parameter index(default=0)\n"); + + fprintf(stderr, "MODE := [ transport | tunnel ](default=transport)\n"); + //fprintf(stderr, "REQID - number(default=0)\n"); + + fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n"); + fprintf(stderr, "FLAG := [ noecn | decap-dscp ]\n"); + + fprintf(stderr, "ENCAP := ENCAP-TYPE SPORT DPORT OADDR\n"); + fprintf(stderr, "ENCAP-TYPE := espinudp | espinudp-nonike\n"); + + fprintf(stderr, "ALGO-LIST := [ ALGO-LIST ] | [ ALGO ]\n"); + fprintf(stderr, "ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY\n"); + fprintf(stderr, "ALGO_TYPE := [ "); + fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_CRYPT)); + fprintf(stderr, "%s | ", strxf_algotype(XFRMA_ALG_AUTH)); + fprintf(stderr, "%s ", strxf_algotype(XFRMA_ALG_COMP)); + fprintf(stderr, "]\n"); + + //fprintf(stderr, "ALGO_NAME - algorithm name\n"); + //fprintf(stderr, "ALGO_KEY - algorithm key\n"); + + fprintf(stderr, "SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ UPSPEC ] [ dev DEV ]\n"); + + fprintf(stderr, "UPSPEC := proto PROTO [ [ sport PORT ] [ dport PORT ] |\n"); + fprintf(stderr, " [ type NUMBER ] [ code NUMBER ] ]\n"); + + + //fprintf(stderr, "DEV - device name(default=none)\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"); + fprintf(stderr, " [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]\n"); + exit(-1); +} + +static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type, + char *name, char *key, int max) +{ + int len; + int slen = strlen(key); + +#if 0 + /* XXX: verifying both name and key is required! */ + fprintf(stderr, "warning: ALGONAME/ALGOKEY will send to kernel promiscuously!(verifying them isn't implemented yet)\n"); +#endif + + strncpy(alg->alg_name, name, sizeof(alg->alg_name)); + + if (slen > 2 && strncmp(key, "0x", 2) == 0) { + /* split two chars "0x" from the top */ + char *p = key + 2; + int plen = slen - 2; + int i; + int j; + + /* Converting hexadecimal numbered string into real key; + * Convert each two chars into one char(value). If number + * of the length is odd, add zero on the top for rounding. + */ + + /* calculate length of the converted values(real key) */ + len = (plen + 1) / 2; + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + for (i = - (plen % 2), j = 0; j < len; i += 2, j++) { + char vbuf[3]; + char val; + + vbuf[0] = i >= 0 ? p[i] : '0'; + vbuf[1] = p[i + 1]; + vbuf[2] = '\0'; + + if (get_u8(&val, vbuf, 16)) + invarg("\"ALGOKEY\" is invalid", key); + + alg->alg_key[j] = val; + } + } else { + len = slen; + if (len > 0) { + if (len > max) + invarg("\"ALGOKEY\" makes buffer overflow\n", key); + + strncpy(alg->alg_key, key, len); + } + } + + alg->alg_key_len = len * 8; + + return 0; +} + +static int xfrm_state_flag_parse(__u8 *flags, int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + int len = strlen(*argv); + + if (len > 2 && strncmp(*argv, "0x", 2) == 0) { + __u8 val = 0; + + if (get_u8(&val, *argv, 16)) + invarg("\"FLAG\" is invalid", *argv); + *flags = val; + } else { + while (1) { + if (strcmp(*argv, "noecn") == 0) + *flags |= XFRM_STATE_NOECN; + else if (strcmp(*argv, "decap-dscp") == 0) + *flags |= XFRM_STATE_DECAP_DSCP; + else { + PREV_ARG(); /* back track */ + break; + } + + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + } + } + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + *argcp = argc; + *argvp = argv; + + return 0; +} + +static int xfrm_state_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_info xsinfo; + char buf[RTA_BUF_SIZE]; + } req; + char *idp = NULL; + char *ealgop = NULL; + char *aalgop = NULL; + char *calgop = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsinfo)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.xsinfo.family = preferred_family; + + req.xsinfo.lft.soft_byte_limit = XFRM_INF; + req.xsinfo.lft.hard_byte_limit = XFRM_INF; + req.xsinfo.lft.soft_packet_limit = XFRM_INF; + req.xsinfo.lft.hard_packet_limit = XFRM_INF; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&req.xsinfo.mode, &argc, &argv); + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&req.xsinfo.reqid, &argc, &argv); + } else if (strcmp(*argv, "replay-window") == 0) { + NEXT_ARG(); + if (get_u8(&req.xsinfo.replay_window, *argv, 0)) + invarg("\"replay-window\" value is invalid", *argv); + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&req.xsinfo.flags, &argc, &argv); + } else if (strcmp(*argv, "sel") == 0) { + NEXT_ARG(); + xfrm_selector_parse(&req.xsinfo.sel, &argc, &argv); + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + xfrm_lifetime_cfg_parse(&req.xsinfo.lft, &argc, &argv); + } else if (strcmp(*argv, "encap") == 0) { + struct xfrm_encap_tmpl encap; + inet_prefix oa; + NEXT_ARG(); + xfrm_encap_type_parse(&encap.encap_type, &argc, &argv); + NEXT_ARG(); + if (get_u16(&encap.encap_sport, *argv, 0)) + invarg("\"encap\" sport value is invalid", *argv); + encap.encap_sport = htons(encap.encap_sport); + NEXT_ARG(); + if (get_u16(&encap.encap_dport, *argv, 0)) + invarg("\"encap\" dport value is invalid", *argv); + encap.encap_dport = htons(encap.encap_dport); + NEXT_ARG(); + get_addr(&oa, *argv, AF_UNSPEC); + memcpy(&encap.encap_oa, &oa.data, sizeof(encap.encap_oa)); + addattr_l(&req.n, sizeof(req.buf), XFRMA_ENCAP, + (void *)&encap, sizeof(encap)); + } else { + /* try to assume ALGO */ + int type = xfrm_algotype_getbyname(*argv); + switch (type) { + case XFRMA_ALG_CRYPT: + case XFRMA_ALG_AUTH: + case XFRMA_ALG_COMP: + { + /* ALGO */ + struct { + struct xfrm_algo alg; + char buf[XFRM_ALGO_KEY_BUF_SIZE]; + } alg; + int len; + char *name; + char *key; + + switch (type) { + case XFRMA_ALG_CRYPT: + if (ealgop) + duparg("ALGOTYPE", *argv); + ealgop = *argv; + break; + case XFRMA_ALG_AUTH: + if (aalgop) + duparg("ALGOTYPE", *argv); + aalgop = *argv; + break; + case XFRMA_ALG_COMP: + if (calgop) + duparg("ALGOTYPE", *argv); + calgop = *argv; + break; + default: + /* not reached */ + invarg("\"ALGOTYPE\" is invalid\n", *argv); + } + + if (!NEXT_ARG_OK()) + missarg("ALGONAME"); + NEXT_ARG(); + name = *argv; + + if (!NEXT_ARG_OK()) + missarg("ALGOKEY"); + NEXT_ARG(); + key = *argv; + + memset(&alg, 0, sizeof(alg)); + + xfrm_algo_parse((void *)&alg, type, name, key, + sizeof(alg.buf)); + len = sizeof(struct xfrm_algo) + alg.alg.alg_key_len; + + addattr_l(&req.n, sizeof(req.buf), type, + (void *)&alg, len); + break; + } + default: + /* try to assume ID */ + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&req.xsinfo.saddr, &req.xsinfo.id, + &req.xsinfo.family, 0, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = req.xsinfo.family; + } + } + argc--; argv++; + } + + if (!idp) { + fprintf(stderr, "Not enough information: \"ID\" is required\n"); + exit(1); + } + + if (ealgop || aalgop || calgop) { + if (req.xsinfo.id.proto != IPPROTO_ESP && + req.xsinfo.id.proto != IPPROTO_AH && + req.xsinfo.id.proto != IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is invalid with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); + exit(1); + } + } else { + if (req.xsinfo.id.proto == IPPROTO_ESP || + req.xsinfo.id.proto == IPPROTO_AH || + req.xsinfo.id.proto == IPPROTO_COMP) { + fprintf(stderr, "\"ALGO\" is required with proto=%s\n", strxf_xfrmproto(req.xsinfo.id.proto)); + exit (1); + } + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsinfo.family == AF_UNSPEC) + req.xsinfo.family = AF_INET; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +static int xfrm_state_filter_match(struct xfrm_usersa_info *xsinfo) +{ + if (!filter.use) + return 1; + + if (filter.id_src_mask) + if (xfrm_addr_match(&xsinfo->saddr, &filter.xsinfo.saddr, + filter.id_src_mask)) + return 0; + if (filter.id_dst_mask) + if (xfrm_addr_match(&xsinfo->id.daddr, &filter.xsinfo.id.daddr, + filter.id_dst_mask)) + return 0; + if ((xsinfo->id.proto^filter.xsinfo.id.proto)&filter.id_proto_mask) + return 0; + if ((xsinfo->id.spi^filter.xsinfo.id.spi)&filter.id_spi_mask) + return 0; + if ((xsinfo->mode^filter.xsinfo.mode)&filter.mode_mask) + return 0; + if ((xsinfo->reqid^filter.xsinfo.reqid)&filter.reqid_mask) + return 0; + if (filter.state_flags_mask) + if ((xsinfo->flags & filter.xsinfo.flags) == 0) + return 0; + + return 1; +} + +static int xfrm_selector_iszero(struct xfrm_selector *s) +{ + struct xfrm_selector s0; + + memset(&s0, 0, sizeof(s0)); + + return (memcmp(&s0, s, sizeof(s0)) == 0); +} + +static int xfrm_state_print(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[XFRMA_MAX+1]; + + if (n->nlmsg_type != XFRM_MSG_NEWSA && + n->nlmsg_type != XFRM_MSG_DELSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + parse_rtattr(tb, XFRMA_MAX, XFRMS_RTA(xsinfo), len); + + if (n->nlmsg_type == XFRM_MSG_DELSA) + fprintf(fp, "Deleted "); + + xfrm_id_info_print(&xsinfo->saddr, &xsinfo->id, xsinfo->mode, + xsinfo->reqid, xsinfo->family, 1, fp, NULL); + + fprintf(fp, "\t"); + fprintf(fp, "replay-window %u ", xsinfo->replay_window); + if (show_stats > 0) + fprintf(fp, "seq 0x%08u ", xsinfo->seq); + if (show_stats > 0 || xsinfo->flags) { + __u8 flags = xsinfo->flags; + + fprintf(fp, "flag "); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_NOECN, "noecn"); + XFRM_FLAG_PRINT(fp, flags, XFRM_STATE_DECAP_DSCP, "decap-dscp"); + if (flags) + fprintf(fp, "%x", flags); + if (show_stats > 0) + fprintf(fp, " (0x%s)", strxf_mask8(flags)); + } + fprintf(fp, "%s", _SL_); + + xfrm_xfrma_print(tb, xsinfo->family, fp, "\t"); + + if (!xfrm_selector_iszero(&xsinfo->sel)) + xfrm_selector_print(&xsinfo->sel, xsinfo->family, fp, "\tsel "); + + if (show_stats > 0) { + xfrm_lifetime_print(&xsinfo->lft, &xsinfo->curlft, fp, "\t"); + xfrm_stats_print(&xsinfo->stats, fp, "\t"); + } + + if (oneline) + fprintf(fp, "\n"); + + return 0; +} + +static int xfrm_state_get_or_delete(int argc, char **argv, int delete) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_id xsid; + } req; + struct xfrm_id id; + char *idp = NULL; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = delete ? XFRM_MSG_DELSA : XFRM_MSG_GETSA; + req.xsid.family = preferred_family; + + while (argc > 0) { + /* + * XXX: Source address is not used and ignore it to follow + * XXX: a manner of setkey e.g. in the case of deleting/getting + * XXX: message of IPsec SA. + */ + xfrm_address_t ignore_saddr; + + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + memset(&id, 0, sizeof(id)); + xfrm_id_parse(&ignore_saddr, &id, &req.xsid.family, 0, + &argc, &argv); + + memcpy(&req.xsid.daddr, &id.daddr, sizeof(req.xsid.daddr)); + req.xsid.spi = id.spi; + req.xsid.proto = id.proto; + + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (req.xsid.family == AF_UNSPEC) + req.xsid.family = AF_INET; + + if (delete) { + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + } else { + char buf[NLMSG_BUF_SIZE]; + struct nlmsghdr *res_n = (struct nlmsghdr *)buf; + + memset(buf, 0, sizeof(buf)); + + if (rtnl_talk(&rth, &req.n, 0, 0, res_n, NULL, NULL) < 0) + exit(2); + + if (xfrm_state_print(NULL, res_n, (void*)stdout) < 0) { + fprintf(stderr, "An error :-)\n"); + exit(1); + } + } + + rtnl_close(&rth); + + return 0; +} + +/* + * With an existing state of nlmsg, make new nlmsg for deleting the state + * and store it to buffer. + */ +static int xfrm_state_keep(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + struct xfrm_buffer *xb = (struct xfrm_buffer *)arg; + struct rtnl_handle *rth = xb->rth; + struct xfrm_usersa_info *xsinfo = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct nlmsghdr *new_n; + struct xfrm_usersa_id *xsid; + + if (n->nlmsg_type != XFRM_MSG_NEWSA) { + fprintf(stderr, "Not a state: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return 0; + } + + len -= NLMSG_LENGTH(sizeof(*xsinfo)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + if (!xfrm_state_filter_match(xsinfo)) + return 0; + + if (xb->offset > xb->size) { + fprintf(stderr, "Flush buffer overflow\n"); + return -1; + } + + new_n = (struct nlmsghdr *)(xb->buf + xb->offset); + new_n->nlmsg_len = NLMSG_LENGTH(sizeof(*xsid)); + new_n->nlmsg_flags = NLM_F_REQUEST; + new_n->nlmsg_type = XFRM_MSG_DELSA; + new_n->nlmsg_seq = ++rth->seq; + + xsid = NLMSG_DATA(new_n); + xsid->family = xsinfo->family; + memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr)); + xsid->spi = xsinfo->id.spi; + xsid->proto = xsinfo->id.proto; + + xb->offset += new_n->nlmsg_len; + xb->nlmsg_count ++; + + return 0; +} + +static int xfrm_state_list_or_flush(int argc, char **argv, int flush) +{ + char *idp = NULL; + struct rtnl_handle rth; + + if(argc > 0) + filter.use = 1; + filter.xsinfo.family = preferred_family; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + xfrm_mode_parse(&filter.xsinfo.mode, &argc, &argv); + + filter.mode_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "reqid") == 0) { + NEXT_ARG(); + xfrm_reqid_parse(&filter.xsinfo.reqid, &argc, &argv); + + filter.reqid_mask = XFRM_FILTER_MASK_FULL; + + } else if (strcmp(*argv, "flag") == 0) { + NEXT_ARG(); + xfrm_state_flag_parse(&filter.xsinfo.flags, &argc, &argv); + + filter.state_flags_mask = XFRM_FILTER_MASK_FULL; + + } else { + if (idp) + invarg("unknown", *argv); + idp = *argv; + + /* ID */ + xfrm_id_parse(&filter.xsinfo.saddr, &filter.xsinfo.id, + &filter.xsinfo.family, 1, &argc, &argv); + if (preferred_family == AF_UNSPEC) + preferred_family = filter.xsinfo.family; + } + argc--; argv++; + } + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (flush) { + struct xfrm_buffer xb; + char buf[NLMSG_FLUSH_BUF_SIZE]; + int i; + + xb.buf = buf; + xb.size = sizeof(buf); + xb.rth = &rth; + + for (i = 0; ; i++) { + xb.offset = 0; + xb.nlmsg_count = 0; + + if (show_stats > 1) + fprintf(stderr, "Flush round = %d\n", i); + + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_keep, &xb, NULL, NULL) < 0) { + fprintf(stderr, "Flush terminated\n"); + exit(1); + } + if (xb.nlmsg_count == 0) { + if (show_stats > 1) + fprintf(stderr, "Flush completed\n"); + break; + } + + if (rtnl_send(&rth, xb.buf, xb.offset) < 0) { + perror("Failed to send flush request\n"); + exit(1); + } + if (show_stats > 1) + fprintf(stderr, "Flushed nlmsg count = %d\n", xb.nlmsg_count); + + xb.offset = 0; + xb.nlmsg_count = 0; + } + + } else { + if (rtnl_wilddump_request(&rth, preferred_family, XFRM_MSG_GETSA) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, xfrm_state_print, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + } + + rtnl_close(&rth); + + exit(0); +} + +static int xfrm_state_flush_all(void) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct xfrm_usersa_flush xsf; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsf)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = XFRM_MSG_FLUSHSA; + req.xsf.proto = IPSEC_PROTO_ANY; + + if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0) + exit(1); + + if (show_stats > 1) + fprintf(stderr, "Flush all\n"); + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + rtnl_close(&rth); + + return 0; +} + +int do_xfrm_state(int argc, char **argv) +{ + if (argc < 1) + return xfrm_state_list_or_flush(0, NULL, 0); + + if (matches(*argv, "add") == 0) + return xfrm_state_modify(XFRM_MSG_NEWSA, 0, + argc-1, argv+1); + if (matches(*argv, "update") == 0) + return xfrm_state_modify(XFRM_MSG_UPDSA, 0, + argc-1, argv+1); + if (matches(*argv, "delete") == 0 || matches(*argv, "del") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 1); + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return xfrm_state_list_or_flush(argc-1, argv+1, 0); + if (matches(*argv, "get") == 0) + return xfrm_state_get_or_delete(argc-1, argv+1, 0); + if (matches(*argv, "flush") == 0) { + if (argc-1 < 1) + return xfrm_state_flush_all(); + else + return xfrm_state_list_or_flush(argc-1, argv+1, 1); + } + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm state help\".\n", *argv); + exit(-1); +} diff --git a/ip/xfrm_state.o b/ip/xfrm_state.o new file mode 100644 index 0000000000000000000000000000000000000000..8fa15f78ac75e59408bb6470a917cd6f4ca53fbc GIT binary patch literal 21336 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 RUACie^?S-z2d;j%^nY6VXm<br literal 0 HcmV?d00001 diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..bc270bf --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,18 @@ + +UTILOBJ=utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o inet_proto.o + +NLOBJ=ll_map.o libnetlink.o + +all: libnetlink.a libutil.a + +libnetlink.a: $(NLOBJ) + $(AR) rcs $@ $(NLOBJ) + +libutil.a: $(UTILOBJ) $(ADDLIB) + $(AR) rcs $@ $(UTILOBJ) $(ADDLIB) + +install: + +clean: + rm -f $(NLOBJ) $(UTILOBJ) $(ADDLIB) libnetlink.a libutil.a + diff --git a/lib/dnet_ntop.c b/lib/dnet_ntop.c new file mode 100644 index 0000000..9500df8 --- /dev/null +++ b/lib/dnet_ntop.c @@ -0,0 +1,98 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ u_int16_t dn_ntohs(u_int16_t addr) +{ + union { + u_int8_t byte[2]; + u_int16_t word; + } u; + + u.word = addr; + return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8); +} + +static __inline__ int do_digit(char *str, u_int16_t *addr, u_int16_t scale, size_t *pos, size_t len, int *started) +{ + u_int16_t tmp = *addr / scale; + + if (*pos == len) + return 1; + + if (((tmp) > 0) || *started || (scale == 1)) { + *str = tmp + '0'; + *started = 1; + (*pos)++; + *addr -= (tmp * scale); + } + + return 0; +} + + +static const char *dnet_ntop1(const struct dn_naddr *dna, char *str, size_t len) +{ + u_int16_t addr = dn_ntohs(*(u_int16_t *)dna->a_addr); + u_int16_t area = addr >> 10; + size_t pos = 0; + int started = 0; + + if (dna->a_len != 2) + return NULL; + + addr &= 0x03ff; + + if (len == 0) + return str; + + if (do_digit(str + pos, &area, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &area, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + started = 0; + + if (do_digit(str + pos, &addr, 1000, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 100, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + + +const char *dnet_ntop(int af, const void *addr, char *str, size_t len) +{ + switch(af) { + case AF_DECnet: + errno = 0; + return dnet_ntop1((struct dn_naddr *)addr, str, len); + default: + errno = EAFNOSUPPORT; + } + + return NULL; +} + + diff --git a/lib/dnet_ntop.o b/lib/dnet_ntop.o new file mode 100644 index 0000000000000000000000000000000000000000..8cb237dc587479059d25e1b4dbb9ca02e24218d4 GIT binary patch literal 1920 zcmbtU&1(}u6rVJW{nW%SEVRNNg0x_lq%HLeB(aU%mQ;%1K`4Z6vP;E$NHRi&f=U|g zPz$|^e}ab!9;6DISZOPWV8KJ_MZrS~9vXT`4`O_8cczWgoOH<Ud-Hqm_vZIzc5*)y zzFOyS5HTF>EOAln}+a?*}Y5Kw3!?(b%}RsIj<rTVp$67W3|E*`ICMN~^}c4zo}0 z+;gZ_M1uQOrf$_@hWvSdo|VkPR<oJCR7a0%a~)o7F79m%&&9kRE&HKuImk*HE1JQF z-z(11+>b%_nQpvm2D*Qpt(oQW=!I`=6*B0unX9;S2CNdoQ1A_^A7twz^V2S32Fp8` zS-{Mv^FiNmB@gr22As2{A~ZF~)*#y~m#(Y~x`+x1A^#@iSbU4<o*A!HDrW8@ZHEeP zkraICfW+Abw&q@30c~d2%2Lc0w+*Ev`e~bf*A>cbS~YBuyn;n^FBY%{Z?Hy<jo+38 zyT(b-@fvgYM7&2fb`E<`gZ&KHe|PVmc?_7<GufKP;-<L(KkKMgi4LXkRQ!4AT;_*- z!55C%5MjI4akv*(&91`M1E>HLLQYt30^g8h%G27=xPYQyc;V@=Jp0vBXWNlS&TM`7 zi1LKb$<V;SMWu7-+Kn!ySM5`Klpde2&+qe}Q#!93F-6m<jd%B}X0*495H)izLG=l+ zG;P~S;iZkZu2N%)5;dk%ov0I;3{frAn2eZdJz)?vnWBaY>vd<S9=%P}XeyB~lC(<e z|F{Z}aSIMfMFM;-_CbU_!r`_ZY`uix^n8O2`gjb#23)6+&)c{?!||%?Yem9hzj>Z8 z@>*vhfHnV>-w#~WB`-I|fdIs+htMxDKRB``j7{?XMW5Ke3m(B2cmOc|34*sj7Gyy@ zB<GF+E@PM~_^7XX$hk)$DDNHS7x?-&tI6QYe4N*ks_+kLoAX=1V()VQO@P%}-^2H> zlk<g<*B7{KF|VGK>`N~Nod>$G6Pz`bpuSosj5wa?!QmuaycBZob-y1t4DPWiJ>tHd z0$jdp7AuxCXe3Efcho4^^GPIPq|?b%B%X@u^j0cK@Lu7-v%x+71^@9lzVHo{aRqX4 H$mjV7qvt@@ literal 0 HcmV?d00001 diff --git a/lib/dnet_pton.c b/lib/dnet_pton.c new file mode 100644 index 0000000..bd7727a --- /dev/null +++ b/lib/dnet_pton.c @@ -0,0 +1,71 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ u_int16_t dn_htons(u_int16_t addr) +{ + union { + u_int8_t byte[2]; + u_int16_t word; + } u; + + u.word = addr; + return ((u_int16_t)u.byte[0]) | (((u_int16_t)u.byte[1]) << 8); +} + + +static int dnet_num(const char *src, u_int16_t * dst) +{ + int rv = 0; + int tmp; + *dst = 0; + + while ((tmp = *src++) != 0) { + tmp -= '0'; + if ((tmp < 0) || (tmp > 9)) + return rv; + + rv++; + (*dst) *= 10; + (*dst) += tmp; + } + + return rv; +} + +static int dnet_pton1(const char *src, struct dn_naddr *dna) +{ + u_int16_t area = 0; + u_int16_t node = 0; + int pos; + + pos = dnet_num(src, &area); + if ((pos == 0) || (area > 63) || (*(src + pos) != '.')) + return 0; + pos = dnet_num(src + pos + 1, &node); + if ((pos == 0) || (node > 1023)) + return 0; + dna->a_len = 2; + *(u_int16_t *)dna->a_addr = dn_htons((area << 10) | node); + + return 1; +} + +int dnet_pton(int af, const char *src, void *addr) +{ + int err; + + switch (af) { + case AF_DECnet: + errno = 0; + err = dnet_pton1(src, (struct dn_naddr *)addr); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +} diff --git a/lib/dnet_pton.o b/lib/dnet_pton.o new file mode 100644 index 0000000000000000000000000000000000000000..e784a7e78eda0aa94c7d6f9e2a5b03adb1164877 GIT binary patch literal 1736 zcmbtU&1(};5T8wAn$&8lhz1nh!%|YTOVXAqMcR~py@M1Hdr%5to9<FEACkO~77Hp% zg{2gF?0@00JxODQ*qaDmyy&rt2gQpNA<k^xCY$Ywpu_U!H@`PuZ{F@cnZ7j>kR)(K zf-z`Y4hxV7v~k*oY3P9{81*nf?B#{j0c+IP-|;^4OVT`wRxihP!V|mQ=-H@E#9r)4 zZw)JqezsTTgY{<LFt+ev)`IelSvln=Grz{oMh~-oFzY?Pa!5t$py9gPKw%pTN`!6X zlwM}Oj_=#sY?CYDIynNfJ{t8Y1)Cui0ytd6p!GdIXP9eBc)U8pwx8aNtCcafxfYiw zGITiJbh)~5R|y%<jvB$LY}BVA0I?lRRr}N(eYH!&Fs%8&=f~)b{kGaj?b(gk)d9{4 zErGPs+tU@<5<?{ZlGr0PDll=w0+Xr<Ja<}t9x#Idvy+pfa$<IFzF!_vht)xOFqs@q zB~#bs#2tM}W*Tqu10$-D9qI>A%d2^=Eu!Y7re1bXNzZ92*H<{GOB&ZeT`ZSDwOM^R zW0bVK4r-yubrtIkl)09D2x_*N&+7&5>hXV|jKMS#yr2$A3(NHi+DvbU7r;fd@m7z? zvPhT|JM_j;ortjGLiY(CC)*)Y;+{H4G`zL0AxAm?HGTwRsg&z8LJQwpQUA7WwHK#< zhe;vu_C$NzKIcDyyc6%7KaHH<QMdg_A7ARv&((>i{@mjAJ%gLx*^&Q&aJ=UZM(m9z z+dDf7Nzh?YA|l`<;waffqB!R#&aZw_%<pgg+sS+uDjS0D4WBKn=RHN!E9lz^^>_S+ zPhm*FmB;yiP^KV=!#C5m)*y7Xup0zhQ)#p35uU~TX+hP5mQj1ZL3?Ydp!3WlUM#3t zuyLW1ZvrAAlhI41LNSvoW;Ol*2_g4|raPo>^cVh3Nml>B%Yvr~y6^plhffRpHxX#Y ARR910 literal 0 HcmV?d00001 diff --git a/lib/inet_proto.c b/lib/inet_proto.c new file mode 100644 index 0000000..a55e0e7 --- /dev/null +++ b/lib/inet_proto.c @@ -0,0 +1,70 @@ +/* + * inet_proto.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> + +#include "utils.h" + +char *inet_proto_n2a(int proto, char *buf, int len) +{ + static char ncache[16]; + static int icache = -1; + struct protoent *pe; + + if (proto == icache) + return ncache; + + pe = getprotobynumber(proto); + if (pe) { + icache = proto; + strncpy(ncache, pe->p_name, 16); + strncpy(buf, pe->p_name, len); + return buf; + } + snprintf(buf, len, "ipproto-%d", proto); + return buf; +} + +int inet_proto_a2n(char *buf) +{ + static char ncache[16]; + static int icache = -1; + struct protoent *pe; + + if (icache>=0 && strcmp(ncache, buf) == 0) + return icache; + + if (buf[0] >= '0' && buf[0] <= '9') { + __u8 ret; + if (get_u8(&ret, buf, 10)) + return -1; + return ret; + } + + pe = getprotobyname(buf); + if (pe) { + icache = pe->p_proto; + strncpy(ncache, pe->p_name, 16); + return pe->p_proto; + } + return -1; +} + + diff --git a/lib/inet_proto.o b/lib/inet_proto.o new file mode 100644 index 0000000000000000000000000000000000000000..d7d93e16ee71d5b68cce4d5a976f4c0d034082ca GIT binary patch literal 2616 zcmbtWUuYCZ7@y0X&1v;=RiS}e*uu6(G|S#a>Yr-45N#&yp~XKh)o^U~_DnFhcic@d z0YTx2Fl<AGzVx9Fc`Wo@Z74L57Nie-^Px~QZ;hy+U_r3de&6iO-DG-4AN<Je_kF+l zeSc<VXExVP7C-4rB#4p(`IvM(g%a{zU&k&8vp{x`ZG`fR_Ii<*?Wg=@k<Z#MC_FZ3 z>+#S>$B{O{pO5l;chF>qHc-*URmy*1V>EJCvf%%OAb%I1@pv>xCj5Iv{-<z@e4R!Y z?KF*M?QAhB+k-{0EZX{cXl=s(i~qiYk-*Na3jU+tDwWF3_TTxll)r>_($O&T7N5-T zqWm9GQ_vSz`9rpU;8wD=;U3$1#idJE?f#W*K&AY?U<}+kXZQc`3S@D0;KZ*`IL+@~ zm|TlXQ2s!$#H`i?jr|c7vNRgeX14`v3tseD7BgI!<agIFCkhTKnW@(sHC7vbzZ@gG z<v@ZY=Cj-T(>LYj*nsa*L7=~u_NQMaTU$QQxHnQuV#knoLgRfa?a@qjG&9&r-sl_8 zScr{}jUBay#!sE!XN@=qoxGKI-GezdcgPw#6O=9Lu^1oz(5d(%`v`HG3svS#fo6?Z zPb;et%z6$B<{5Fy9`lGZ)oc={Q9}pmIVT4$l7i_{rQuZr;)FF8I8c7L$vpouar|1f z8iY)?``XHa47hY}7?}#vKW4o2_-5+z4mc?tkaj&C8{f$IH^D_y!emhLK9G1)`bu4u zaf+{Sv(^C^K#m!&-UmwsN^SO^0~|}`4W5d}zIBmpweNzHFo>9T4G8+OBu?wWV4L$P z0A|h!L(+KEK-^dn>;v~m#jEu@4VdalQiV#J4aq^1Tl$}PTpwM`__tutTzljnk@d~_ zoAKy(Yjs0h4TQXq0o#GM1AgZGw*uC!{(3(QfP59J_S3B29xx2#rO2~QcaWkayRJC) z(XA82|GUE(NS4rW^<KU$@J=2*GD+m3s763rf}bKPUTL_9Ab+Rw<~-(U2Qo;qy}Gfl zo=JkMyjk$P?9|pl-Ls>Bs~+@-Gd=Jxdf-0bn12~Gin^a0V0X*E&;!5T11B?Kz)Iqq z;`k)=J%2iI@?<79Mik;IB&P+G!n{W=1x!RwErfH`sh~mN{to^60%?Zz#!Sd6dVrS? zaon%gF|IUsSP#c*zWvR@vw*w*cl-%_wHIs@ybkm|bOaio9ekG*e@x;ge?sHy`A=#1 zfR<;n2i}r6=9foIAWIq__eb^fy@q35CAX!a`c!-Rqr`C?cEV5Pxv$~7G(05_H1hF( j0>yt%;%5H68ei|{fQIYqU_nk49M<nCD$owTEd750TsT*= literal 0 HcmV?d00001 diff --git a/lib/ipx_ntop.c b/lib/ipx_ntop.c new file mode 100644 index 0000000..b2d6790 --- /dev/null +++ b/lib/ipx_ntop.c @@ -0,0 +1,71 @@ +#include <errno.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static __inline__ int do_digit(char *str, u_int32_t addr, u_int32_t scale, size_t *pos, size_t len) +{ + u_int32_t tmp = addr >> (scale * 4); + + if (*pos == len) + return 1; + + tmp &= 0x0f; + if (tmp > 9) + *str = tmp + 'A' - 10; + else + *str = tmp + '0'; + (*pos)++; + + return 0; +} + +static const char *ipx_ntop1(const struct ipx_addr *addr, char *str, size_t len) +{ + int i; + size_t pos = 0; + + if (len == 0) + return str; + + for(i = 7; i >= 0; i--) + if (do_digit(str + pos, ntohl(addr->ipx_net), i, &pos, len)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + + for(i = 0; i < 6; i++) { + if (do_digit(str + pos, addr->ipx_node[i], 1, &pos, len)) + return str; + if (do_digit(str + pos, addr->ipx_node[i], 0, &pos, len)) + return str; + } + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + + +const char *ipx_ntop(int af, const void *addr, char *str, size_t len) +{ + switch(af) { + case AF_IPX: + errno = 0; + return ipx_ntop1((struct ipx_addr *)addr, str, len); + default: + errno = EAFNOSUPPORT; + } + + return NULL; +} + + diff --git a/lib/ipx_ntop.o b/lib/ipx_ntop.o new file mode 100644 index 0000000000000000000000000000000000000000..03effc229ae8069f980f48dc614bc69f9f20376c GIT binary patch literal 1696 zcmbtU&1(};5TA4t+h|ClR4C|S4^>*QkEAUvdXS`z_7#fw(Sl+nY|||*Hc3eyn4;)m zTWP2P{{+E*z>6NGqBNk=iwJ@zJ(c1?K|P4L&b+*5*X^EkV0UJI^F43h-Z?XIwk;F_ zO(8f9yP8G;GHo7NiW!JO6ta`!^{-RB{`L~Dzv9lPq~nGYtKZOLo4oky`=<2f&0Kc} zNr{!$_?~E>`m`M-zK&8O_Lywh_0I0Z%Qrjx<U8KD6+zzzh&mq=&W~tgWcY^B7)h<Q zkGtS*EjMK#dL~wGqSIzB0*t#G>F>Ob33*{l+Ovr#ld-T&_G~AN-r_<)I<KoLEqed< zb2nvS=U3$RLtR-!s48&hMUvOIuU^YeKbJ!{!NQIRPH#vQwN8A9LJ+DYW9=RH)sjyj zc2rtguVV@yi6`&1<>Fm;ja=Mx!ejB2avB>QJ!STcotx@42iPF%H~Ukm!E`D;Z1!BV zXH9O27T!0+<_ZJ70Ia%P6xIxKQEBmcEmiDAiwV0XfX!OM0ya~v0+XmcpP#E(MH^VD zENq7L`l`YzTnAPt7mIdDczXUH(ZpaHd7m#yM(KJV+MLva;zy8&eHf{iRYJYC{fJQ= z`3)lP!iVTMK7<>jcxYaog9nnLzZRVz<^0$9A&jL`p6i7gfUeJ_w)@ikYb1?=zbCS- zsrmJsG(Uc;d*;s|7BuRkATYr9d;o?J=y>YSOa8P6(dgeD`F~K3SG>U}`{POX?~Xz; zG%V8R7J~!GX?J>mdM{DLg6gN#{^<MglE1#!=9S4W3}0L?re$N#6fKJp*a`I)bi#zI zA$4NK4WJ+EUNZhnziW%3c*xU_zHf&S_un-cwXjsnmqdAq6+nxS&)bzsshnRd7c8+* cE}<jcOCmZO+VM}fMM<?lKW?HAk^ek@174KAV*mgE literal 0 HcmV?d00001 diff --git a/lib/ipx_pton.c b/lib/ipx_pton.c new file mode 100644 index 0000000..1a52b7f --- /dev/null +++ b/lib/ipx_pton.c @@ -0,0 +1,107 @@ +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include "utils.h" + +static u_int32_t hexget(char c) +{ + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + + return 0xf0; +} + +static int ipx_getnet(u_int32_t *net, const char *str) +{ + int i; + u_int32_t tmp; + + for(i = 0; *str && (i < 8); i++) { + + if ((tmp = hexget(*str)) & 0xf0) { + if (*str == '.') + return 0; + else + return -1; + } + + str++; + (*net) <<= 4; + (*net) |= tmp; + } + + if (*str == 0) + return 0; + + return -1; +} + +static int ipx_getnode(u_int8_t *node, const char *str) +{ + int i; + u_int32_t tmp; + + for(i = 0; i < 6; i++) { + if ((tmp = hexget(*str++)) & 0xf0) + return -1; + node[i] = (u_int8_t)tmp; + node[i] <<= 4; + if ((tmp = hexget(*str++)) & 0xf0) + return -1; + node[i] |= (u_int8_t)tmp; + if (*str == ':') + str++; + } + + return 0; +} + +static int ipx_pton1(const char *src, struct ipx_addr *addr) +{ + char *sep = (char *)src; + int no_node = 0; + + memset(addr, 0, sizeof(struct ipx_addr)); + + while(*sep && (*sep != '.')) + sep++; + + if (*sep != '.') + no_node = 1; + + if (ipx_getnet(&addr->ipx_net, src)) + return 0; + + addr->ipx_net = htonl(addr->ipx_net); + + if (no_node) + return 1; + + if (ipx_getnode(addr->ipx_node, sep + 1)) + return 0; + + return 1; +} + +int ipx_pton(int af, const char *src, void *addr) +{ + int err; + + switch (af) { + case AF_IPX: + errno = 0; + err = ipx_pton1(src, (struct ipx_addr *)addr); + break; + default: + errno = EAFNOSUPPORT; + err = -1; + } + + return err; +} diff --git a/lib/ipx_pton.o b/lib/ipx_pton.o new file mode 100644 index 0000000000000000000000000000000000000000..e6064cc98947a64ddd9a1f4bd9683a9861008087 GIT binary patch literal 2008 zcmbtUUuaWT7(Z!lOykOK-E;{0kcZ2tU@y5gXsKn~v{rA+?U1p716{bLw^r6BlAg{C zL<>p8Ll)PE`KtId$6od{6h@adcCq+UhJxUWf?_KQ9fHn3ZT5ZVo@=i+dD=dbd%o}Y z`~Kd0&N(w+?r#YMh${i|30b#Xln_1W(YdOVHWDH1Rk`o?-q6X7x;$Ge8w>14wcK~9 zH}uU%<-WyhE8y81Ir-Qj7lCxNpFkTOoq?apMwELdJEi<H$Zi?zu(D{daple+dtt)5 zWHPF(82KmN`MN~AmauS*Sn#}B2Qf;Al~$uPuC$w_5oN0hTGT92rNb<pQesB_dUutF zys2RzxrT#j6_Y)x?NcN~hS};f<L6o!^@RNE9=-5fzA&d%^yxKKzECO7$%U6d4c6y@ z9VUeZ&Q8B{$f-x5UB#sGK1evAJ%@Oct+HnfOu6s?nc3G%ch>6lD=TbHF5bmDB}p+L zVg1iP?n#lw1d|jeH@nHRC-Q4y`6ASwsumV#SgR~=fl*7JD-uS^H!y$~E&e4JUqR~O zGF7C(@{z!R_H;&`-D}jVOExb#H3ubzQxE&kNpT?9JW@jr^<!jStMs4S8K2(UBmYoI z)aQ*rHIZKpo&JqyU%mu>FtyArJ{8WrJ}95DxE*>!ujPZFOutr_hhT*%H^Uz#Kmxh; zws7l=*i?vb=Zy2{5xN3v+=Jg1{zKold>8uaef@l}ES=qu50eA^{XNl+1D}4eBig0L z)m_nDu~=M-X?vm_pV=c(!=et}xmz7gb?qQTo&0u!T1R1}S!W${wX8jEsnpI<qK;V9 zBI?n}NuqMp9!rj9tqGf`=?t}1sMk43t<={<O=Tt~>@@ZC`hV0Y1mmb1d`Wa+Z)tg# zXn)rv+rZ{q*@x%&o+czF@QtuyomOFQ73;?W_Z)A|9P-r_?hUne0)aXIYy57A#lHOI z-1>)r^NQQQi)o-VoBv9a`9t9A#=G<DfQhXJ+PeJWZ%9yn--CerF?4m~u|Kc)eK!H= ze|PAAU)2B78w|QX9_?nW>S9B+ih%dPF9Lu6{D?KHZ;1ET3A|VQ^}U=|M?Z=DjaNZ~ z1RJJsT!KwI!TuV*a2tfQc=EXS2xbZr{L+mQBw{EZ6@2?v)`<^6{Q&ao0&?#oyu18o zkh>f|kxQPSnY5ZBV|MPCO^J(;WYW%N)0yOWCS}p%nKb(I2dwz*@NB+;YZK)%=qP?X O%F)KN((S`_VgC!-qfmPQ literal 0 HcmV?d00001 diff --git a/lib/libnetlink.a b/lib/libnetlink.a new file mode 100644 index 0000000000000000000000000000000000000000..b4edfee890baef0a507b81577f8146bb81dfadf1 GIT binary patch literal 16776 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 RpKW!~tMm9y7p~6Z{|^nfiMap( literal 0 HcmV?d00001 diff --git a/lib/libnetlink.c b/lib/libnetlink.c new file mode 100644 index 0000000..4cd2b2a --- /dev/null +++ b/lib/libnetlink.c @@ -0,0 +1,580 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/uio.h> + +#include "libnetlink.h" + +void rtnl_close(struct rtnl_handle *rth) +{ + close(rth->fd); +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol) +{ + int addr_len; + int sndbuf = 32768; + int rcvbuf = 32768; + + memset(rth, 0, sizeof(rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.g.rtgen_family = family; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_send(struct rtnl_handle *rth, const char *buf, int len) +{ + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + iov, 2, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2) +{ + char buf[16384]; + struct sockaddr_nl nladdr; + struct iovec iov = { buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) { + if (junk) { + err = junk(&nladdr, h, arg2); + if (err < 0) + return err; + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = filter(&nladdr, h, arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg) +{ + int status; + unsigned seq; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov = { (void*)n, n->nlmsg_len }; + char buf[16384]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + n->nlmsg_seq = seq = ++rtnl->seq; + + if (answer == NULL) + n->nlmsg_flags |= NLM_F_ACK; + + status = sendmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + perror("Cannot talk to rtnetlink"); + return -1; + } + + memset(buf,0,sizeof(buf)); + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + if (nladdr.nl_pid != peer || + h->nlmsg_pid != rtnl->local.nl_pid || + h->nlmsg_seq != seq) { + if (junk) { + err = junk(&nladdr, h, jarg); + if (err < 0) + return err; + } + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (l < sizeof(struct nlmsgerr)) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + if (errno == 0) { + if (answer) + memcpy(answer, h, h->nlmsg_len); + return 0; + } + perror("RTNETLINK answers"); + } + return -1; + } + if (answer) { + memcpy(answer, h, h->nlmsg_len); + return 0; + } + + fprintf(stderr, "Unexpected reply!!!\n"); + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_listen(struct rtnl_handle *rtnl, + rtnl_filter_t handler, + void *jarg) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + + iov.iov_base = buf; + + while (1) { + iov.iov_len = sizeof(buf); + status = recvmsg(rtnl->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + if (msg.msg_namelen != sizeof(nladdr)) { + fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); + exit(1); + } + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int err; + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l<0 || len>status) { + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Truncated message\n"); + return -1; + } + fprintf(stderr, "!!!malformed message: len=%d\n", len); + exit(1); + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } +} + +int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler, + void *jarg) +{ + int status; + struct sockaddr_nl nladdr; + char buf[8192]; + struct nlmsghdr *h = (void*)buf; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = 0; + nladdr.nl_groups = 0; + + while (1) { + int err, len, type; + int l; + + status = fread(&buf, 1, sizeof(*h), rtnl); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("rtnl_from_file: fread"); + return -1; + } + if (status == 0) + return 0; + + len = h->nlmsg_len; + type= h->nlmsg_type; + l = len - sizeof(*h); + + if (l<0 || len>sizeof(buf)) { + fprintf(stderr, "!!!malformed message: len=%d @%lu\n", + len, ftell(rtnl)); + return -1; + } + + status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); + + if (status < 0) { + perror("rtnl_from_file: fread"); + return -1; + } + if (status < l) { + fprintf(stderr, "rtnl-from_file: truncated message\n"); + return -1; + } + + err = handler(&nladdr, h, jarg); + if (err < 0) + return err; + } +} + +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *rta; + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) +{ + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { + fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; + return 0; +} + +int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) +{ + if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { + fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen); + return -1; + } + + memcpy(NLMSG_TAIL(n), data, len); + memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); + return 0; +} + +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +{ + int len = RTA_LENGTH(4); + struct rtattr *subrta; + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), &data, 4); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, + const void *data, int alen) +{ + struct rtattr *subrta; + int len = RTA_LENGTH(alen); + + if (RTA_ALIGN(rta->rta_len) + len > maxlen) { + fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen); + return -1; + } + subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy(RTA_DATA(subrta), data, alen); + rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; + return 0; +} + +int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return 0; +} + +int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + int i = 0; + + memset(tb, 0, sizeof(struct rtattr *) * max); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= max && i < max) + tb[i++] = rta; + rta = RTA_NEXT(rta,len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return i; +} diff --git a/lib/libnetlink.o b/lib/libnetlink.o new file mode 100644 index 0000000000000000000000000000000000000000..01f80682bc682cdc83a51bda1e4847f01e3e255c GIT binary patch literal 11928 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 literal 0 HcmV?d00001 diff --git a/lib/libutil.a b/lib/libutil.a new file mode 100644 index 0000000000000000000000000000000000000000..effedc909bde3a2be9f890ed2b4b3792f2e170ee GIT binary patch literal 56136 zcmeHw4}8?smG8|z2pBU_(Wcd{qa8F@i8BEqLDUQ+`U@MBDk!aJ7$!3$&yY-Kej}k$ z4Lb?VuOZ6ot1fTbw{^F>KA!bG*|m#^T?mA~t?Q;%TdZ5ls<nv`#ZoCCGVh#w&zaoZ z%*dY=x9>Igll<<z-+Ruv_nv!y_ut&#y_d}lM1u`CjK9V`-1vMxZ+TgTuY9`O?Uh0L z)9o&wI+Yj}j~7Ce36Zn*)Bm5cQixOeUtJ=^y7}sV{dytr|7nl<|L1i=?1ZrA7YgyS zsY^q4U1PHyS{jN9bjO=xjZ2$D^~y7Cs<Nk)Dcd(q+2zuTkvFEif%^Jrolk~DqM>kO zD|rytQ0YVrgf=z@Tb4zdLUyPQ9LoaD^<r6LEEb3^g`oS;s*gv8tTM4IU<VsQF$jgU z$_OPQ>gq7@4PPTiJQj+gFG@tsWdRWl#af!~3rVg}EGEJ&QQ)m>Xo=aPA=DbPqm9i= z>yUED7bt56iH(tHOWY2XfkdgRXmg-Rhbgw9u&CYKR2Q{@&6WanKZ+!OEZ7nW=>!VU zD59Z2(=wex0UCuJSkg31h5}L)^|5ecsHuKP1_MaZ)Kq7$pd^4B)7v5`Qy!v{nnu+? zfOI1+%V3B{^I~W{mQb_Z5>c**-2xnuRt2F$A|h@#HpNO?Qm<p`J)GutmsM22+o+yC zuXoxscm;iBW!`f6CN3WTV0G<H*;!feIx3STZV*F0MuqTxX2@MBot2_coFgn}k>{Z0 zG<l9#iFb;vj>DGok>wn+^V)6@@d=xfV63i1o_wpT$x~z{b`<ye_FB$cmh<a=lwvvC ztgc*-)zM@B%&ORDztC1LT#szGRviV#2VLLm8P~TyE4~T*K-{+SDPNB>R}B8|a5%hH z{e67=Ls6@goKpOf6cbUg&GnZm0nb3JQkJi0C?lAFwP!j0Z&UnM`UITy`2REV&*d)! ztAA5CefH0e{waQ_aH;q^GZg>H^#32J{|`d^di`%pCb9mbDh8iC#rl87@h6U^UcTq7 z#L>2u|K#fdB@8}&+}HDLwtR6HdG_Ob-(Tz0V+-(MEj0pGZFfuVA?pV<&hMR<caH^H z@elc1(K__F9rL?3PVl%kZcp?FT<w1kAiVkaupg<wf6JB`;HY(etD1<89#{Jd7;<1& z&iH4sMS&9X^&~zT?|P)CV|)DOTIcOr=dG<crjQ&RJ(B8y(p|H&iU!?@j|!yU{`{sQ zAOXJ3xnR4hw^w$4v2%7-c=zn=WD=SsXs1f>_4Yb@YP;$^<+b3hgf`3Sg;ZC0vJ(AG zu8vxWUwX5p_j>l`43vR$EmJnTI?&IQD_k94U~kSqF*qSruJ#0FD_hT2w#=5RqY0=W z2f36!l$Gm&jJAV5Zh<goTerV&=D6jPtl%&Fn=tpYfZ{5@>#3gZs;s_Qow-+8$z86E zh3$LeFArP-9BoHS<71P%6Um$hej{THuN@eJvE&q3&g-bd0+6EX(Dl$;CqUoNRC%%? zDcNCXcJ9FQfTZeQ#`^FP*M}154xl>totKQt;A-E6#jx=LF8+h44anS65>{0tN!WM5 z-f|5*QawjUPmQzF&H)*g^IDbjy5+n_DR+9~b6gv%MdHYKSNp#MPvQvZ`W<kDcUNb- z#oUeq_LQ3Lc$U?@(v$0Zk=WuzuC3MCUGqJ;6@9M64}ttS&`lDl^ZdK2#Wl`8)%at7 zh83h3D~KB^NJ+0halAmjY<5vqXGrX-&JysdLTr$_RQG4_$o|8%aW>Q|s4Y1V6?mXl zL0vhFO5Zp;XW)QxsH*pS<xv&yw@mUHI54I*F@RMp>@3J0u+RZmkr;5HQx=!(G84zn zyB+)Ib2-zVyL>&pmh-zUkY7ma^=SEeWaw5L2uPRZ{8EMwzV;vGhsZzNtQwHlV8r0I z=q>N)iI4B9^5lXx-FNj;Iyl-9>CyWC#5*vyI2Thb+nCK>aBWE?d@JNWDYHN<5NE#S z9HR(irdYZ`it2*)UD6*-CFpm)9?7H>q_OaLWSD%*dH<89hw)ZFw!6^eet=D{9Cy_? z$Elp&vx57Sv!Zu1CU5!Yj$0M4TF$%Bo?6Z;haPu*YdiF7C4H_feOA{c9<0*&P9N4y zH9pfS3*_JL{L~Mj-F3PCZAbI`iT#(>cHfa(-LcocVpexSH|o9Cd9Bv@#c?aR*Q(fi z=yBDQ{pEJow|dIA$FHu227O{p@cn1qKq_Amf7gV6^iP@abk(G7KimH-7L{vHM_)YK zx7XRzyFiwr@4)TWs&|T^nO1oL8PjBd^!oO0#@5aUW6j+X*jx`@XgR9;tW`UZ%jvu8 z?q00)YlpaCjOl7`140Q|-HSX$iWwkiLbh#5=Aiv+st({9Drn%foL+mX)m`f;u)62N zc-GmiSDwU?Y*+jJQji+hb{uftc6BU+0-z+xG~RW$)7LvJN%6@tB{iF`fmuNCn&wc= zv-UNS7);RWYn}O?qFSfcQ@pFn;}!$|1%zct`@oG5wrZs(zjBsKxT=~w1p_fasQzxn z_f07>u_GV!B3lCWg!)~QNakA3&ug5$_Ou5sO(e(L@F*ZEyL^+>4>ocLC6d|k169tp zTIa_sv81;qxCi`Ou~!`ev2o3&(ZccY?p#kcX!k7~7zFpIp1}1?w(M|#H|#(UGsHWT zME{+B=MVln{0nNFkNoqY6IpOOzLJS!(6pa`_-cahL9bUe`NXWUs>w-we1wnVt}Q)& zXV;vrOFodv?|9X|{`lZ{c%Qp2w!q_s)-}JXV*lFsyWkpERdFQ#8>>53U{xHbb>6d_ z0eDZ^yMR^E$NRRHq%!C9!Ylw}#H{Z73#vMU`Bk0wkF$2qcFUM>Eb^|*$(*iS6}gks zm4{(-9F>}6k0brbJ+@5^i)4UKtjak7@he3@r7y6$%9Y2N?Y6q$9}4iUyZwv&cl+=0 z*Y$Q?+VQHZ;|TNsRb6%PR)S{`JgXke7Xo^a4(uEJKg_O4>@4uh;*p*AP`f_ZhVz$w z*q-cn4q<Zw!#QYM9O%7m*|e0Xq}Mnv)i^IyJ3Ap!vpNnxQ0lymZ+&WnW=~GFC9);K zo)Nl7o4QA+VRprXH$dgF@9cQ>!R^%!#Pfcw^SbjIXq#K1G@Jvdx99L}AMD1O>4KUW z|L5nhLJzEf8vY!P=muKQg|!dHGeKGJRpSly73$9&e&?`%LACFIfBpi?dHHtdCs>30 z6(3m61kcy!S9d><g!YrFT;jv*2mCP7n&5%)1hw6X!E7tBQ&rFEiucs$^TR9{op)@v z$HB;8M)D_Ca;HDJvnIKty5fVCo8_E#cu|r3+o3gu{?~Vy{H~2I--~TWXV|yEDDC1w zoDU~=yS8lG_IfS^Wo^fxFShTsuaslG`}TMHzc({)c|~I2PM|}gE!&tbz7O)d>R1x= zu-sEsvFq#kR&ZBxSLcLc;4cR9!Ec4vcKf#ie_mo>`rxC`^RMj%!j*ZJv+u56|D=Of z@PzMhO|Z|(QmxqFTlJxEAl_sP>nmd3ZMV*@yQO-5-MrgpTh&!!VoXHE#H+mJt?Gde z_*Znd-FoYMpnwLAn=^6ajW>#k-m+FmpAgm2XiId4`|^pg%iYTYE8I&$Ze09u<H`uE zEM4th61Uxr@E3D8w*cJK)UrHO4>WGO0kB{Tthcq=rC2khA$fu3VPcRy)EWr|ZQzXt z?1oSj_<;!HmCTvap;q5cJsC#)iv8r#vmPKxHS;+0AInBLo~@Sp+)bc0RuZoRmb&Lh zSGZduc4G_Vv?0_KxqO74qD$k;Ld~`+0MJr2^woG{G*r)ooF9{Jd9<Z@=?wS87!eX3 zEHO4U1_O3T3Z!xcv7m8xECi)h@4he46c42ejQB#0vP@((0P>WWtKBWpRN4aWrGe;@ zz*6L%K;@k!vRaD@#^$fZ@dKDk=wEu^aUVHuDRi&SPUOh&nH&f<_SI<g@I?xePgo?D zMh|BU1Jdz-SFy-vy6_)q;a?-SF8|-O@a1X3k89x(BisZ2OGp<OG!~mkFn6xWS)H56 zt1X<@EfJ)v(&+x4+$lP*qPqm>nl!pf@S~|JEJ~mt3}17S@F%tKn~)idANg-B^dkTB zTKFUxD&<QThQFhQW4h5m{L;qo0?H!-S4wnv_|+!~ul<;CjIT)xUqbTdtj<Z~))u<g zP#D(7Z)<d1F3A6+hR>zEK>k-}TZP3p7jDRhYAN;jl16s{#iQr*_geU)sc^4!&4lL! zki%v%#WSn0=;7?@!s1zl?(W=&a;ghU*5p+cdi{mvRfUy_v8%@r1EzDn#{V+mb5`di z#@t!xeJHy-XHBjU7|%+L&LKL8XI5eHLpc~3#02s93%#qyU}WWqu{RY~5*?P;k2L-b zgv<JX?we=Np5ZRJ`IZHf+*3-YmX^88yxyrkuW!1$<Thx$t$?lY$<s>1!6{H(N@FXQ z!9qRQc2wC7?23k(0;SkLh|>Ci9T25U;J1p>XbXCf+*j%w?kxu|@`M`d!qLF8kSK*t zCsYclnH;kN!Fxq1{<H$^C8hezEjO@Y5wZSRQYAU|yBJZns_jGA#o&bE7-9B*K|$6y zW#d8)#sdREX^P3d0&K*!qL3kOGa{ll<GLDfl$nX&%@Gp6n{2jx;K2Cd$G>5Q<DWka zRkR2p51LB&PsiU%=`Yvb6{P3nFh9yiw?QU0oR8E(>@zaP{3=w4Tft%K1j9-uD~e7M zjQYp5Pv&R+-vbz@MRBNS=S8}2$ZgU#Ly(CfHS?o>bz{<=2Z2TwCHb=yKleK<!{kT5 zsXL_I@Hxe?o+>u3$e<3){A2Y@7yrV`6#q92;b#1bYUC5?a>e^N_#I(#(rOITkuD{O zrz>H^<+Lr0eDiG<0`r-r$zh4uUm)&gDXbvy=g1nv0Yi3<ls4pNSB~J(8Y4nrJv(2^ zFOP#ffTw%KrxAS{*l2j%G8^3K=<fhr%BO-^iA-RyUZ<mv0*>@;M9=bZ{3GR1c{BbO zOiwtElN_d}BIA1cUzwip2>8*ggMXBNkZ>LkeIM|2>3kvs{=*D7F8;{$Q_=G{>L-M6 zkUxB=_6i{J68xu&_tgye8yWC^z-7J+`G*MiQhmA!{Ah5E1nJudpCnNs&V%wum!4w4 zk$wZwW4%G+CVW5PJl?vJ@FJ}6U>GkYywbp@5gs9YiDDBs0G=*Au#hg(v!CcAM8AOW zdGreK`0AbvazYvK<_x%<0lz;3{uh9w-X1$gC6LE)I6q03uXRM<L-dbQzWy!){r|~; zKa&C9M)DV9BMfFS_|g0{1O2`X_^&hIZ)Ly_WWYbnfak({o=!h5z)_z)a%o=(9+zI6 zfqoLvKUS#n=_pG~&p>}8(R*pUwSnm8W}u%B_<7=YNp8p4zBdC-n=*m|29n{%Xv{A4 zislAeZrtI$LUnaGU~Op{+VnDP_Yeig&2ZRIlnOG#PG^%5L>_F2(iRllS|Ycb$Soz9 zj*<J1Vs?Ef8WrIP?DDb0BGlTb@}nqEvL`9LJlbf7PH}4zB&{h#yJOPrP=fS@n(q^_ zW=cvJHUTw@a4Z;T4vTQGsU;RNb}_|Pz{Vg5)ORz<-8OQ2O=?rk(2g5(tJ5-Ef(nXd zp=H6y3IVEs?MxyZZi>emFz2`hYRL*rt128=$q+FG<FrBFD8-VIC8lkZ!o5K;%Fyub zQxGZ0(zi>U>MkmA9o1{7rUGB3<FGu6<~a?=SAg;B;5|iy^f*>z{5cIDMvsJG(9&_a zM4JY`j{4&f>B)eX(2Iu6ygvH6hj5gGV;)ZDS`GiKhQCD2NRLBRroWc@Z&QApa8v$6 z8jf3{Sk6uj$0nEY&D2k$9G(8hgk!wK3NC1$HTpjTKg-#t;h)#=*ERf74S(B^KM%st z9MkA820zOmPyITlr&Cfx9UvUjucxP2!}auBuHkxmt|FY}_mTW6jb7K!mo;3EH(<!g zr+V3-(d+4nYPg=BZ)mtKe~lr(lH`9!qyK`Yx4+kLUH)bb*X3_D<hPRiT^hZf{yq)Y z<-e`ry8Pc8@*gAlM>Tp~eh!VRuzYp-pVM$%zK3uw$2}x}l18t~pQ7Qq{5cw~%b#z^ z=gH=mHF{lsP{Vck-_-C)T6sN^0ly711O_c^Do9--Ksf5>YWU~(<=zbRkqq>!G+b|o z|HhC*!$I-5Mz6>F!wmFWGSK%L^!U!Bd0C^M41UhnuMB$BHJaBo`cm*S{c(fdjl^Jb zC?lx1Yv7;hCuq1=!>1CC;!yun;2%weMz8B3l7aqf8R#E0=&x7e#S<Dmu1Rq^-!SO^ zoam2h^iwtZ^RUqegZaG{{#njk4ad1Q<9|yyio-6;4gYA~)#%H?&-9<6aWKlS(C|wM zH|3OQ^eCU@cr(zKXP}?0;WIQjmWJaPgXQ0$;kahZcu>Pp_l$4W@EbJz4Gpi<aM*M# z4eDXKhVLhw>(8x<OuVDf&(i3BiGvj|D5nbkIX#OtdaQ$tyTOeHn>*xTQ%MfS+sMDz zz$GJKZ3f;(?dM|#&XM#OcpF8&-@sw<NEwosN)O>~1Lt)S%fNXs5;1UI&*0zNSpGp8 zPi!#gc^#t9z<C|wpn>yxLlNbV<@0)j*T8wbVV;5WdPBs(dA(tsf%Ce;1_S4Hg?$Fj z^ZO$P&hz+U(l@7{=idtroaeP|2F~->9s}ojD$HM+;RlM;cK_5~|I{x3;d}kd;F}!v z)y=8z_1}Om7MV(ESHjr7K;T}SU-<8+aZdObz+MX8qGdT#JzJ2>>VDEw1bXS09^9X~ z!___+G(meEV86!}tk=9#Q|`#hgK*czcFAZtgEQT(Z*QMD&ZRcKyV~)+v68*6jpwu- z#h|vM92W0yJNgBQj3?^&U&BU+zpCxVFAZRiRsn6_fSp{m&VWo5Y}2ymQy5?>)$}O> zhLwLz*d>|6;$hgjSOELWIzJ0jE7(lrdbkK2shvDQNQ~8009sPHx18Lcf-+c7u8t33 zpH6BwjoQMd?ecMLoU8V6rgkB|QcD{)X|vkkk5R>&QCTuiTd@Ro%P9{)Vqg}?S9{g> z%kA2{TO0QJ^y3b&Rmmif!H#Xe^YZcrEBKz3n0WwY2>UA7%OagnTK4rHvzax#R&5o6 zot)42e-*N?F~)ae`f7q7IInC*sgT#fqP14=cg|7vQ%-7~{Tbt+s#oKDAZuUZog!99 zN7BArY7-*=tfkW7cgDazt;CKZ)?{zjg&23oi*{AD@6Ew;*ZPw?{Hu;*&L6C(3BCjS zkSL?C+Hb-pF5YPjGGDeF9Hc_cos5>>DINTNe>W&~@Sm)1+mr8m(YH5M+OUHdwg*b( zTM*erwH0sM=e5lc@dE35jLfw<0NRmTAqN(L3pAttGM1mEiV~wfsdL~C=(3bN+}m|l z%llkRQO8~=Z~q@<dE-D#bR7PK%KPq5t-J@mb?j4lU*#aUBl*YrzE-Ih8FvuR#y7U$ zKlH->ZT}i9z>R0}!Y)p|uy@H9R&6gl^Y?Y&ACADcB4_*ktWx{pVY7Dp$Nje&Z|Khd z{Pws?HOSS_NPZ&a+oYB6Uf+wU1{uFGtb#Tf>e1(FEBfsV)klW#jUhA-^o^B0UNy*A zzJ8~0;7ed<EZ>3O9sV@l&#Erj>A<#LC_mies`sdW1m&la2RorZ`Tlh8<{~JYR0*EE zOT7owqe}40;KFu))QoCiR0E?L7}db221Ye7s)11rjA~$11EU%k)xf9*Ml~?1fl&>N zYG70YqZ%01z^DdBH884yQ4Nf0U{nL68W`2Us0KzgFsgx34UB4FR0E?L7}db221YgT z|APj+0&Zr3!yv)Bx4C6`vsl{HvIMRnX>Mr_!PO@9aQ<P?7Wamt&7mf-q@~4%&-?>+ zV^A!GgAMCNG$58mZSl3x5^ybPjMl?}3Gz~tdePbvjlf};-qwk&@@PbIxW6DK)=NNI z;Jqef2e09~P_BWGdF0(6F(4ZmByTc_LF$&nv6UeqFIj=(9`6+~JV;@LBzdh#Y$Pr( zGl>Z}ya2MgEYJw&JkXh*BSg@>CYTRAr?U!<OIs3%g&+rb#AL7~8jVNrBuzMr4G)0C zvy9M~XEEY;e|X*-oA2T`C}8+ZIy{cfJxiVx2j|h<T-cY72hy#{>&6ci!Jynl(xo1s zC$|S|Tf=+EJ!ef$B5xiXJD01@F2wILzpK&xg4_`QLwF`$H^%Rm@#A55V^-(k33n>q zmo&aP<i>d4(eNH}r;B$2<rPUzC*E3(PtR|?hJReVYc#rjl)h8R?+%UcM&iT#{+ot> zT)cRG3!DAqIF)#3YJ7Tr@thSlJ>*EI=am}WFNyAC@t&<S2IbL#e{2U0f==A<kHx5; zF9BXri4QPO(6`&lH}V`F{8n7P7)(HR<!t5f(znX^84$*kPvLkT2bwR!KjQQ$BSOm1 zH`Jnp0cE>~d&L#tPZxiLKJINp;b1U+jKho{&%#4&MEvw|c|C;bhWQ!8@3k=s^Q=Pt zINeL}Gd+9RuFk;k#RdwDTRw*#96$4a8G_KQRdq=`kNEkURhGc~?4J*gbox6;hk@w& zLtf_R@(TjSX<?bt*1iwbag#Q75`HWn-I)A%{>+H71I$P9uhf~rZStev)E%-!@Pl<Q zmGT=sAwMbifZxnN9u<);{tcB707msT%8gGm{-KcJE~NY9&MKr#437fRZNByDJjN_N zN0i}9fmh<J0J8CX28rXh7-$IhgHz&MQ3$_{aW^>7usm?4JKJyx;Mtnic${z_;g69* zxqs*eJPS^Tpz52d+kZ_sSkiE`kAn~C+0XWqfMdLisc^ZU-$C^F-k{-k=jY%?Iffd< zYlItW5N`sG^5;>}aQcrE{X($Ou>3;EC(3tAbVzL%5nf{8c+NA*FQRYnx!;~j^gMQA z`8N@c<%x#%a0}t(3QpnkN&Z11<n-fNd6>?{M9=c^>^sCO4LRQ=+%j<V=A>eyK(f-E zX{qSB(-@*(3-Y;4hEO3M&46S5N=N_w4EU27@TW82n=|0s0hedlfsLj|K81KO1O2N+ z-$soWk0S>&(Elz2o(KH$EJv#69PdShN2mZ<{*{bVy=8hlQy1lUsY%&K>8~Mt9@Rsp z{|eznRDbppJ)V_^a`@~9rpK`y;(c^B50B^Yur_trK@gnsI!L~G5f6MONX~ESG(g?& zr~&FqNDaVA*ANE;zzKYfc4MHa@oO*<)GnU<7+3@Ya7jyPIo^p`TESc@s*!9%IPp!B zeAAgvrFkUZ5KeqkCEvBo2Wf|^D@XDT;lx)a`KB-*>v1Hf9zwic$?Jnd?BpqXaN1o= z#MD`P@*qMy>y8fHGmZ$<bt#Xx;{*Hj0KDlE&mvqF89LW+=txAOR0>2H!vN$ZK&eWj zP=H3El%i26AVtAhPf;*{6ksl)ddz3c)R7<0mqG*8Sa{%{&y1->5DelI;h*uR5d_0> z0?Mn7ZU8sZUjhG2|00Dm{eK}pzb7XBy9T{Y{=;OO^y8uZK*RFyS6*>G;U+!yGiVUM z68<@zU(#?rze_Z{M5AxlaMU5od6;lakKXEIyN(9qy$b%B{&9_7Hg#?Y1oHz8$0jWy zQMis!lY>nl_fPK;j&bU8j%C0v$Ao}E3*~TmPa)ita}(jL=kFjhm^(7yiwQ^TYWU~; zKBD0mFXP|S@KOzbLc@_B|IutWa8Calgq!I<q|xj0PiVNFo;>PzFj$ZGat+td1}oKY zojy!B>P<Rnz;&;NWBKAZ5Y1N&Ih-%PzW~#NBnNTe3#N_eF~2BhBZ6SQp~*pKKhbv? z^jjpg5Z^NB_Y?i227NEl{||${oCaEdW6<v*`d@3fbdnz4&VZj!1<m@OM{>SEc&dC! z&NRZ!`gv0Ze4Zv>ub&+nj?My#_hAj!>(5_lI65Z~{XZHwr~hY!o9TZ;qu1rXtKsP6 z^n7689Pjy93BaJ<^!oXE4M$d{zk~2p{iH$WT^f$ge}R8A0YeVw>wAQ!@<no<$bi3S z$eFLii#IaRA12%^-!U|<Gw~wAQ4f0gR%<xwf&0Z<HC)&KS2P^yJ(SLvfphx5O}MFt zztiY-`A=!Mo}P^c&hh?C!}aq0wTA2IyokoPsq&?9?Zq07s{IY+>oUUmz2kgct>O5} z9&{-NFX5P9l=G2-#dL$dpXjeM=yRxlnq|=QT_QIb^n7OOgBmWI5X#p#GvH5a@=-qv zDV;kr(EprpvtGTG0q3*i%=$Kw(#873x`^f)4cF_#3=PM0PL<R`+-~5Uew*-AeItLn zMz70%Si@01r{_@v=XjsiaJ{}ACOlQID4ic_IM#<ED&H)cAF&=d{WlVxN<YcrGgeJJ zX2{|7f{qMw9wnU1aT2BHaRcY^%mz)q-fsLx!%=TDDV^_WxUT188jkc;M1MXe1Ptqe z(?5l9GrzMndR_i48m^~jfq`?paShkYQ9s*NPp3*Hu8pYon*F@=!8qna8fxzur|hc( zu7QAYTo;wbtt`f=8yEQoE>DpG(jo)DLa~ct1D{N|+rW7pywbq&JW@24TUx1f-l)7{ zo`KIIe4&BgMEGI@uO+;}z`sOz#K6BoIKeVKDIG)DVhTsSA^sRTz_cnWMc+gCV+P(v z!uA>XVj71XH1G|C<J}r)I9^_VnP=d<ZrEVp5o`>=;JOzYmc#2Zd=@j~ygs<!py&0P ze9A|Pp4y#C1Lt+Xg$B;+IIItr!|OOrO?CE)$f=)IIrZ9#GPg9Repcl^>a$TFmH2>X zA<P<Yhx^>&n$4cU^VC^*$!%7mrzmms`sL?UY(ld5fxE1(Hgxq4okOT!{|h%_V(2Lv zQgMo!RF(2m6-fE9ozM*xZ3@z|B{AwUQ{hxpDL)MjQvM54F6H=);pes@1sfU2GW^+w zpT```pKFBk7=y!cjaxS^!_R$OipEf2mf_cbcSasu>(tE{!!IX%8bOv(AlZgL$MEMG zexBR1$T3FvSi_%h_{SOk@rM5#!(U+d&o%sohTmoQiwysHhW~uSe}UoG?-;~9UucA1 zWcWX0`1xF1*4Jl^@Xs0kiw*xJhX2nD|K|<=rH0>a_%Ad3=9ytjL(QQmZ2f^NJVH?s zQdXd~>{<bf$8b@t2sQ*-Vxln=3YB}yL?9YO5H`#--7D&wf{ox@2A74{AfsiOB=b$F zm?~mTjS&%)Pkc;MZdeSCMenN@f%-ti7E!#t1Y>GgF5`<tL<5KvO@YV~QD2Yiq|v}~ z2{lGQoE>k54NKDm++0{MV)p{l)Y1}Jg4Y%{$@GNl>l<OiQW!T5iN?q^_4N_46m}Fv z8ka>wOGQItBm(J;H3+<@6><Svn*y+)3-N|=&k(rq5nkezPgn;AcbR-nk<Y2}d98dx zG9Yxie3r{+g?##mS#kTygbg>(DCL#<eB!>A#(Htr#QM9reoP>Txwi=Cn)uJAnjB~Q z9?6sK@%VIZj}*cU`NgKw>>nxtL&N$|zLCFoT?_$c--ER4anKBAv3&Ax#Y4Xpa)0qf z2*>!1*GD`?eEW?6H{onu0giO>=NBl7KI#b3i=w!n<M@4q8PSNgbV7mFgr@<9>F2R5 zIHh6!6&R=)_Od+(!q9DYFI*ty8ZSCI{S0IIm}$bWedj3(&L`#{`+VkS9M=*!Es8@u z%gqWwfhKMJN%&E}x-t1NnWpZMzLDZzrZa=v<VXJq-2t|c=2mWEI2SO4%>3h=C|&%m zRDZn?t{XFcJxq5a-5<lA4E_B{w`T&=d^-0*;`@%jt+L_v;Yt8#y2(MxP7T_F(5Xcl zqV@C>tXEJ-9sWF)x_OJ|mgbxr<)dMo%aPwZEL(ilZAu!i1z_Dk9{x>(`H+m725_aO z#4=}yGhK-cRrL<>Lh|!_$#UdT;1FgR^q(XDLIb~y{0#=q`D`_CTpvKwsjO5wvHd}_ zPFX3O^S|D}{p8<d;ML^cW8k-tf1iQhN&bEVUqt>R2EK&+JdS6*H4v`PWl_&9gqI*8 z7^aUauP8V0`w6!Uyp8aM2HsA1gMojO@KytVi11DW|8In^GjJ}C^#;zrDQz<F$4Sl} z1OEZx`wX0a^X)fqF4rRl{?8;wpQEFmcM)DpjVQ7q{ygC&2L4mRaSsO?rhi#^g=OHc z5U$VlQT~3y8w~n432!y<LBcx?{C&dL8Tbc;uQ%}Hgl{r%o}cb9@G+#veFlCG;r#~A z^Ufm%j(e}r@Vt%nhI_Bj@O*~x%Ly-`&WZ67!pjZ3lyJ+y`L}ibz7W*^45Du^=qm|t zHE`VXg{ITMd7t?@1HYZ<*Bkg(2;XGj0mAnfIPX{AXW%VF@0O`j^^jC95$Igxrx2wr z*T@y1lih1F)mP?~gzi&cE9yr@lBtw7uNC#cT2Xl0zvlZ6yPo;i!E=(k<YLi@t6;IH z6A6be7QrgfW<=@A3d@0|B6Y=yIv3u*pvL*gKOe5sSl~NwyYE%FVIv=e?z9rUMTw8D zT3%*#J%{|Y&ReoMh6^TMcRiCpD7<Y?e%S9+3*U=ybw$3@r!KGP#na5+;^VM8dN#uu z*U9*={H|xJCClJXkU5iCYzF9xhcI((CDoY{Uta!j0b>k*((ucHo5qqwW4jczIOUR# zEW@8|_;U<@uHnx!{9_FNSi^s9n;2%kWB3ml{^_fRk&9og9_|lz4)@m^{#wJ|Zusfd zNC`2DXT*3nH$|x&TD6wI7$bbF;m<exIHuN(IWEL8zixQ!#MnjbAp4|ijfOtW`i9FF zA_AMr;6H#X7IHNNMx$a$<g0Sk00VLLKoQi#CtdZ;fx7x8X+@O}KGkXh?)t{SQW1>D z;A5^JY#u`?YLzB{PPq6L{_mS6B9Ta4ePb*r9kEah*Kd|ZS^{W?;qxy6t2}j6gj@lF zn_#8442Hph2rbDh4>Z?-5XlcKH1cyRx%vX2wC!je$|w`dT9&{ET&)5YTkL2{lUi<} zr5B(m^?eDGG<9e><W$!}s!0F57IHQCd0dG>Hig4?GTrzc)S8^CLig(2*@Y#EyqgMP z=5+Jy*)!ZFH{Y^gl6y+&)Y39{nb$kj=k-l@m)r)QeOd6`m&7MeD-8#yOcJ6rwqhA< zq5~T)GM07&yX1liWYZR<|F5i_>>&NHWxfZQ)=u0wVFA-4pQpce;-&_`{GP*2^4Pip z9Ok=^Vf!imd<s0>wUa*Tt2rLF&A2kHoiKla(U7s1?KvPA-Ddl?pCW4Dh{-nFscOK@ zG$ASPi+~y|&FQY4SY`uCayZ@O74_gSF$`mV9}|*o(qcQBPSq<Y{xe-WX`lwYfg(KJ zwUa*TpXZ$<{YG%gi_!iutexoZ&nK~Vf@=Z#+DR4V80Y+E+kin>|B#;b$nEX*8R)GH zIIgs)!7wcPH%XUTgh>SqUxQ)tI^3s>JE`HDUHp{*Z*h<NrU}|sm|Ba0Xr(D38=DWT zm;Am>B^;{-R?V?gudX8;-(Te6H5$&RDQBfdk9Csizd<<DlUF>c(c`)e({s8o8TzYQ zO!SO%80U*|9^aH3^!%HtW#H$Nf1!c%IH$qDdCjQRz*+B|29E7Gnsv&OZ3?Cx>kpds z%1Yr}9-9oD%VUp$&m#Xm1Fs=}zk$ys{}BVfjr{pk{;W@4ODZ<-yNI6WyiC8C@N$Db zOt@v>_Y%I)z<G^{+c}oc^{>^S|2oO(H1H0>*BN*>;p+|j5yCea_;(23W8jYwzR$q_ zj_`g1e}eEM2L2S``P5-zFxHcQBD~nZdCjQAz;_URxq-hxxMko!Cw!rS|C;ay1OF}I ztp@%c;hhHld&1Wl_=kkAH*ns2;HGfY1CdCi(!ja>g?2GeUmxWq1^3C;6w0QRsjE^? zV@-kAS1<`G9hLAs@_4u_wZ{2Z{~dnku=8_uTWXcliwgn%#ED$Xndx!0uL2gh)~?!j zpt|DsU$3{iuJ#O;!WCbhn&3xP@K9|QTtEf}F1<?(X8RL6v#sP#*T#HWbwD0(b;q0b zzu@(tDHdPP5K|5?<p7g>-+_K*AFe6XI!E|MOn+VPs$<yn!!@d_PUK5R&$zzzS@D8Z zCvZoCs{`*tb6&B!rh1T<8Ec(mTd@TEbpx10*kWVBRibd$WRvHh-}#`{u{}rdcDsFW zS1evQ+P|d~$RPU*E$0`O^Ri44-p6*U)xFYFv>9bt&btzZYg9XM)zzQ)C`aDt^*{mK z-ZlZQ^W2n+WL=9qUaPCgQ(oIu@2RW>msQ(kd*(s<7WSe(E8h9~oolzukRspXm@KE) z9<vMBlfA?5hE){FJ@LtK$ymXvV@cp!epyX$ht=`wtj-Bu`y9*p6_f#pUHOE+yULR_ zumW_#H;+1RZvh5g3L3hBOdg;yLp~?AipwX)F2^G@8k_HfkId@bO--_5LI*nmxxtvf zbK|`25*eZ%2gnU`w})}=_E65v+8G*re#Xs(l|<(Odl9+O;&skB-8rlC<|f8Gl>Mc` z#dz$+S(@{G%y9^x)=%X!_ilJ%dof1(h8~sd)Eg4OiWn#RVz9B=nPy+ULdsD%7#z!> zlVDMdAQ<fH5y$bBZYqs<In0dfY6w7?XUdOr5{#dJcLS$196yd_bwk;aJcw7rKVoKb zxesWAFx_ze8LI?`nI{Y@qB#eTc~E~?M$FIpyA?1_6F5_*-%JpNn6!0(n;4QZ|C!F& zc>huZ#mkl%7y8pBbq`g@4a9(RRvf3A`A5twdw@hM5#ECy&6x3PLBpI#_sPxK(v>i# z8%HZ6O}euQkSg;x;i!e-2J4GHH{<pZ*B;Gx7&+Y;@T&kvIS0W;!|f!lb)=JXx^p?K zzv<-Mkpagkp6nZN>L*>p=X#B>L94FWh6Pd%=1E^eR8IJixz5Mm=NjMR3UL#}iw0jm zy>#xDsH&?N#&fUx31@od;juE-ZJqv7s(^@}tI3(B;riUp(Qus}zsX0#@nU(S;cq%o zz6<_2oo{GzP~VK>H}hy%4#tfJznMpaa&TRS>E(EaaEz02H@MLt8}hJOOb*7m9?5dj ze$hJl*wZ)dbbrs{aWW>MQl>+VJu}}cd>vL_y61fpI_>HGXu(j-a(3FYtuC*;cy%j+ z>dJE!-W7)-=F5CV{l{I>KJ1crzMlRC_463rbH3nq%lQz;!d;Uwo{j^p7g){-8K;b` zqsJMrzv$YSlj!%(9JkzyW5ltWu>>vWHA&&xINvk&S^N<UMvGNHmOqv4x$AED6%M1| zi`!+cdL^0tZ(WseTl^F)cd{l+X<dI_qz1Rno?euR1{1~x_D8~j0WFi^kTz*DjoCa9 zs*l-tuYq}f71C8HQS$j+a>Kg4zpyxwvpO5bc8L9nkIiP%J4)&q(T-*sn?>>!$2#K2 z_ZXc7pLA@tlK7aO<IiOW*_p;>Yl+`&(3|~0rm-3GbDPIvNUY2YV%6}EnBE>C{umK9 zphq)iekvi%%oCF5<EsY7t1LOTJ)N;xzL_AB!|5ll$TT)%{xcn$RZ#q9{>}K)=?>Gz z`viDB2FGUDH?jVBY?^6owtp;25#nKGrJiQ|sQ|-)bZ2vHhJ9d`R`OggaqOg@52e?Q zbYnR;$zguBCy^ZHXB%J`<I%tuJ(@Y4u^-N5^syhevwpxi4Vq|y>zL{AuVlc3fTR4! zz(&LEa}?a^<gCbmw`IVEHaG~1W_6HEnP^mwDU5g-@o5ZlGzbg(vPW97qB*{734EUb zi~h~Q$cmwDnL{z8eiYyz1j=!k$n1e-p`kttUmKij@R*0^hS(hFWr%AzXpmkX%gvUk z8f)qFHH4e;Z_#jlET@k}bv&WTr&mlO-I^RMA5Q-dG#vBA<{5HuzPL<(L^!7NeE4TM zuW9%N8qVK5Aw7->nEo<)(M|bR63%)cvEphC*YoQp0t`kTHWoP;e~kR~JU5^f&y$Tg zeHEv_hsv!fvQoxd3Fj${gOS-HPd_dK9)zt_xa4;P26)AZWC5)9)i}?)I&k3Lj|{G7 zV%fVTXSJ`#*W<h&-Zn5koVYIAzQF3b#DhzF`M9))W3u92zw^H3^oRYc{ymv9x8uz! z=LP$ft*BVvi_V_#AO0}6V!yK+DD2*FN7B_1f>^3C$p0K_r^<P8PWQ?@5%&M#5K?YK z%KzT&_tqqPz~4C*{m!%fkf|zX4^W5y@cyh_Re7Qm2&#Rr0*&OCjIQ=YFfa^v?6ofj z30siK|8y1*c8+!SxSrYtX$w!<^=!RL+(8g!u>YB2kLOi)yeeg=M3Mx_=z3}!%AgFU zk@3n2nFJ-{LP&=k7pD@j9l0^*po28r-vRF7xp%dH12CCqF|ZPat1+}Js@)<0qOeqa zJw~~7-|Y2o$m*;X&JkH0uBUc~Cv6*O0|~&Ad0o>3Vfq;P9IDuJR_7#gYYW|LxT1dw zW8Y76A<_-`#&R)xze>Q+upX3eWIvN>tXobEp_dr3=3qSLSmg@Bwbxg2)Uv*%Z|I@7 z--u=!>z*n<uBTwS%;@G3PzhnW;q)_xV?i@b7}h}f=P?=PA7^&V&v+$Z7{6)Onh7ET zlXf29CWfTUf2L#AZ502PDPFeBxX_<2scx!J2Z<5q448K_|Cz?B{Ec`C@v~*d&$wwL z-Ps(gVtnbwo(k7T0f;D*ZY+u8cYVyr?^~wzLfA=23vH#iosbvZ>gqz#Xmd*)Ztt@j zTbkwiKE{e=gF5Chz)wkmChi6rG-i3qC?a-C^QrdT)2E#FdQpj1fhuJxRNo!m4VB2X z<umZfd~$cki}n?Xx4nsD<CcHk^~@OmCVZok+x)Jldb0Y$&KRJRRPkTJY6-8mEW&QT zN$c|uLAP&T+K-W_PDK7PhQHnAV@IO;b{TAS+nzCMJOJx9;oVgpNDNx=w)ZcA9us~g zyd|7i=^1n5@|&!6Z8M8Y<2S&HP;nM==JwyHNWKWa-sOhj=ewNc?(ptvC=b^&AZpwG z@#D5{LS=`Y1=$1lV-LJ`C>hYL;UEK(2`1CJ4&r*Bg+DE#L2RmYsmC^Q-vYZTvlGzL z{)zQrSybm)B|NTXf2KaHf!eS$?Zc{!csV|^zsS^wohpB(KFp%{v5(LVx512M>ciHh z$^Yr}VZ6@8@vv>G1=D9@NXz_Z+K25Wp=a8Mm0)JU<dYoi!>}!7{c#_jsSo4xPfnJ8 zj3b@FXwPOJhJBBI&Na6mn3hkXZ@UQOVBc1W0>NxkmT-e@zJoGosI%d|Ext^3a@YgG zDXc~(NAhsJ1Pyj0*eh^fhwC3`Ox!Kek$qjcfmf0rHsi~mws^zNN$0QADo(o(tjy2{ zs?T+x*Mi?Begae1qC_&+)q!87sa~q%RqCUlXOi{O)&4vL*20F6{4F>Pv0#%*es_V~ zo8|AC;Bo!<XKz~-`=F=F6R!5hfFkj_3wqV=S<{#2bkFj3j;%?G<O?fyOGcn{wc~GX za`I^<dwhp22LxKd7i72}e(@;CmBO~YoeR|Vm(qySNhxZ3(r~)uE*Wv+MBW46=a}t0 zj1{)$>?nfyFz)f`m6@jLAHcpIi9-4^^^w@N>V4!ye5t^|ew8)ZRfWZ?@mqsL?#=j_ zfi!<&eIx6GazEm|%+xn7Ow%`@YR$Is3cwII=XVWM4jeDrr`k83Du1TFk@+o-m~2iz z!<qWVPRc*`0jE2^<NXyJ58I|%GWCtjf3m*iJP>T^9^?9Vis(%H#!BiN4^qNUcYeox zBi0YynEBVkbT`tS&At)i`Xu_m0+6H6@A!SY5b(5pV5+kT;zwX=OGTQF58q2P*lp<Z zyCPDciMx>)3<hgmjf_K^-kp3es@Ge7y88htwF+2?i4baj=N#*vwRL9R^6{0fExWs| zy)bd>p7p(%dG}q^J*)3otXc3Y``aq84+53zf2HPkxSR`nJ6C#+)HpBULT?{V_F!-4 zA8^BGKkoKC2wi45beZ-Pt>-Z2e7LAvjQcg+xL>oRwreG9<V@@+SDnpq)xWC6%O2Qi zpr(LA%ym9GWc^?__OSxG!ReLlhh2|s_w`h^ohWrZ($lfs)qWI$YLYu4bgpRMD?{6k zs=)Yf!6hevt}2*QR{IVEf35S8a|pWETvz*>!0OuaVgEi@xLkkG+3xCi72|<jxUg~7 zpWL-=f9|+Gzmw}xvYnqu>9C1+tn05K?s#weUVE&sXW&AZS>hBFIbC~@K}Oo~qO0Q= zh@Ojc)tc_RvtYOHTGy8AU>j<GNJbgo4pGbv{$_S(?o<eHc4E*R74$#H?ez7`?!0<x z+jTQs-|q3lC&XF3urD_6fgeff1K$HW$hAxQWUesZLm8EM?6?HdhExOdz|r<$>A)PY zA(xr@@=Wtx5Af*o-IwVdnDcP<n!I}oD<8_~&RsnwG4@Yw-g}|`qJxgi_9j!`-YV<# z(C_Tnx1mb8Z{$8NQ{T?}^MPfUIn}=XRQWUY?aaS$SnNaTXE;;ezCKO<PiNk%fA7U{ zp-t5?1<gx?VIcFLao<kme#U(}jcv|!-fNNmCZ}?$9A^HF;FK5X&Su|^ee&6y_u_A{ z`n;FhBb<kr?;t>MNELMSvJ(!9hEOZ~44y*L&Uu?9)5v-4-(<kugri>dCLz;&_x}P+ C6t$HA literal 0 HcmV?d00001 diff --git a/lib/ll_addr.c b/lib/ll_addr.c new file mode 100644 index 0000000..ea3d660 --- /dev/null +++ b/lib/ll_addr.c @@ -0,0 +1,93 @@ +/* + * ll_addr.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" +#include "utils.h" + + +const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) +{ + int i; + int l; + + if (alen == 4 && + (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { + return inet_ntop(AF_INET, addr, buf, blen); + } + l = 0; + for (i=0; i<alen; i++) { + if (i==0) { + snprintf(buf+l, blen, "%02x", addr[i]); + blen -= 2; + l += 2; + } else { + snprintf(buf+l, blen, ":%02x", addr[i]); + blen -= 3; + l += 3; + } + } + return buf; +} + +int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) +{ + if (strchr(arg, '.')) { + inet_prefix pfx; + if (get_addr_1(&pfx, arg, AF_INET)) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + if (len < 4) + return -1; + memcpy(lladdr, pfx.data, 4); + return 4; + } else { + int i; + + for (i=0; i<len; i++) { + int temp; + char *cp = strchr(arg, ':'); + if (cp) { + *cp = 0; + cp++; + } + if (sscanf(arg, "%x", &temp) != 1) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + if (temp < 0 || temp > 255) { + fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); + return -1; + } + lladdr[i] = temp; + if (!cp) + break; + arg = cp; + } + return i+1; + } +} diff --git a/lib/ll_addr.o b/lib/ll_addr.o new file mode 100644 index 0000000000000000000000000000000000000000..12a2dea9d5d65f874f3b71a0409155a88e772eb9 GIT binary patch literal 2624 zcmbtWO>7%Q6dv12>ZYk}P$Y^#VyQNeqGsiFqmV`gdDFO=AeZvfv_(zoZv8_d9H+9k zkct$DjH`}ZmmD~A=>@@sQ&sg4ES#cV5WN6J2nmTE64FBWRTa^g_trDXWbFtEp0qn} zzWKiQ-u&#?Z%3jB+%6Xp<|5CM^`1Zpx!_s%!?qhH+es@ijt;RejUxv8i+ybj8;6Hq z9$*!kzvDIXt4);cQ~mGG06n7Lj_7wId;d6>pvB$ll17V7>QMYXjsG5XRj*+{qdK2o zavS;UZd$$Wzv9))3kaho7%AMgzVKf;NY*5Jd0pfIkq1PAKYf$t=e<#O@A#-OVvHIm zj96u6g}@B_?|(M4>b0Hn)|(%?&a};}wqmz%yjrcYZ)vexMXeA<*~+JGtSz>PwWLzB zl%>@lqVQTa*q>2msrM)wQEv^g)hJ7-3v(LKVEV@>`-ZZw?22fiY#pMdX*F;SV^Q|A z4HwG(!Uxbd^7lPgA$#?$w&H-=Zm^cCO-L(_s6kpxtG&@;LJdd3qtT+Jz5w+dtKfR> zy>ss9>|CE6@)wbbvWnI6ImoLOS~{$@>E$^fK++ncdgDx25#X7XDxhyXJ`|s)h41<= zv<Izilzk6-0AZ&;GD-ugYw;`;MT^hW^gr2g_@>zPF1`Ze9l_8P>9gM*Q)FjHZl^My zQ^qr|o9Xd{l1`h6L{@7hq#dJxxrl2j(6+@p%MaN;z#g?nYOlN8@An1Xb4PqTE_nuh zO24nu@C6NDZ+^@9mW$1$ra@nr^HgX@xQ|VM$9=BkIp2IR-*U<QqVMEISkS@!`}>s6 z!RLo}DSNbTEu@5k!EQaMKdp4WluRhpv}$-)kCuw>*+qz!J3C>Sr=Yd6wRTK+*<{+( ztmKqMw1jDyL_3ws5iL839i;1;4qYTA$6~3hIgun<X3|P(kiIKtneo?%7N4A$NM<al z^#A25pvI}UIHH6_ACI170m(=A5qR*}IqjCWhV=4v-3DEBIZSqN!4n*pt_gc2?8wXM zb-8xK0LJ{UdL6V_R|p>2ME)mb5PUwJwKl>rXSrh>c*mg^^A~U!oJQ*xfU}PltThDJ z5BEsu#rhorOw`1wcC+9thz~lsv47Fy`pD+gKLCTywMYKL92}Fy;5+r$Z&d0Hejq>L zf;Ql7g~vI6#2V$F;=~g$E}JvIJSO|dYa+kmZC7WWx>?jHo`#iReT{Y)@#ebN2zctg zMiF1kexuA|ugpda&&z{So#K9c2yo{&j7nt_{3*aO&N8%U;-2h5-$pjR@YZjH(@k(Z zzm51uHo-|c9kYKM;=+q%LMF*&PG`q6R*H;gl2$BZO`cvGHbWT#cWZnsOJ0RB6vuRu z%f-!13I-C%Y?h?BnvesK7Y#o@d3Q!_w02f;>~H+#1$&d@f-f|sP56)T1BhpSyA-EK z!hI6XBwXfS=D5g<`;GRo#P>sA)cK=?<9Z8x$q@&0qx~lF@f#O>`~M8|v3|@ca0Prc Rl*!-oNd&<JzFg~)e*nwLnCbuk literal 0 HcmV?d00001 diff --git a/lib/ll_map.c b/lib/ll_map.c new file mode 100644 index 0000000..89c0d20 --- /dev/null +++ b/lib/ll_map.c @@ -0,0 +1,170 @@ +/* + * ll_map.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> + +#include "libnetlink.h" +#include "ll_map.h" + +struct idxmap +{ + struct idxmap * next; + int index; + int type; + int alen; + unsigned flags; + unsigned char addr[8]; + char name[16]; +}; + +static struct idxmap *idxmap[16]; + +int ll_remember_index(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + int h; + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct idxmap *im, **imp; + struct rtattr *tb[IFLA_MAX+1]; + + if (n->nlmsg_type != RTM_NEWLINK) + return 0; + + if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) + return -1; + + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); + if (tb[IFLA_IFNAME] == NULL) + return 0; + + h = ifi->ifi_index&0xF; + + for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next) + if (im->index == ifi->ifi_index) + break; + + if (im == NULL) { + im = malloc(sizeof(*im)); + if (im == NULL) + return 0; + im->next = *imp; + im->index = ifi->ifi_index; + *imp = im; + } + + im->type = ifi->ifi_type; + im->flags = ifi->ifi_flags; + if (tb[IFLA_ADDRESS]) { + int alen; + im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]); + if (alen > sizeof(im->addr)) + alen = sizeof(im->addr); + memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); + } else { + im->alen = 0; + memset(im->addr, 0, sizeof(im->addr)); + } + strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); + return 0; +} + +const char *ll_idx_n2a(int idx, char *buf) +{ + struct idxmap *im; + + if (idx == 0) + return "*"; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->name; + snprintf(buf, 16, "if%d", idx); + return buf; +} + + +const char *ll_index_to_name(int idx) +{ + static char nbuf[16]; + + return ll_idx_n2a(idx, nbuf); +} + +int ll_index_to_type(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return -1; + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->type; + return -1; +} + +unsigned ll_index_to_flags(int idx) +{ + struct idxmap *im; + + if (idx == 0) + return 0; + + for (im = idxmap[idx&0xF]; im; im = im->next) + if (im->index == idx) + return im->flags; + return 0; +} + +int ll_name_to_index(const char *name) +{ + static char ncache[16]; + static int icache; + struct idxmap *im; + int i; + + if (name == NULL) + return 0; + if (icache && strcmp(name, ncache) == 0) + return icache; + for (i=0; i<16; i++) { + for (im = idxmap[i]; im; im = im->next) { + if (strcmp(im->name, name) == 0) { + icache = im->index; + strcpy(ncache, name); + return im->index; + } + } + } + return 0; +} + +int ll_init_map(struct rtnl_handle *rth) +{ + if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + return 0; +} diff --git a/lib/ll_map.o b/lib/ll_map.o new file mode 100644 index 0000000000000000000000000000000000000000..62facd97c4773458cefa07c1355eb6f5bdb5d3fc GIT binary patch literal 4216 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 literal 0 HcmV?d00001 diff --git a/lib/ll_proto.c b/lib/ll_proto.c new file mode 100644 index 0000000..98c67fe --- /dev/null +++ b/lib/ll_proto.c @@ -0,0 +1,129 @@ +/* + * ll_proto.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "utils.h" +#include "rt_names.h" + + +#define __PF(f,n) { ETH_P_##f, #n }, +static struct { + int id; + const char *name; +} 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) +__PF(DNA_RT,dna_rt) +__PF(LAT,lat) +__PF(DIAG,diag) +__PF(CUST,cust) +__PF(SCA,sca) +__PF(RARP,rarp) +__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) +__PF(802_2,802_2) +__PF(SNAP,snap) +__PF(DDCMP,ddcmp) +__PF(WAN_PPP,wan_ppp) +__PF(PPP_MP,ppp_mp) +__PF(LOCALTALK,localtalk) +__PF(PPPTALK,ppptalk) +__PF(TR_802_2,tr_802_2) +__PF(MOBITEX,mobitex) +__PF(CONTROL,control) +__PF(IRDA,irda) +#ifdef ETH_P_ECONET +__PF(ECONET,econet) +#endif + +{ 0x8100, "802.1Q" }, +{ ETH_P_IP, "ipv4" }, +}; +#undef __PF + + +const char * ll_proto_n2a(unsigned short id, char *buf, int len) +{ + int i; + + id = ntohs(id); + + for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (llproto_names[i].id == id) + return llproto_names[i].name; + } + snprintf(buf, len, "[%d]", id); + return buf; +} + +int ll_proto_a2n(unsigned short *id, char *buf) +{ + int i; + for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { + if (strcasecmp(llproto_names[i].name, buf) == 0) { + *id = htons(llproto_names[i].id); + return 0; + } + } + if (get_u16(id, buf, 0)) + return -1; + *id = htons(*id); + return 0; +} diff --git a/lib/ll_proto.o b/lib/ll_proto.o new file mode 100644 index 0000000000000000000000000000000000000000..491146cea570c4c4819096e4946b517bddcb5b11 GIT binary patch literal 4120 zcmb7`U1%It6vxkI^Rb$^*%m*7itLNJt#-(4(lmaOZIX5(QM7F^5W{9NJKJ5dyEE=g z%oY?YYipKT=!+2Oiw{18BIt{xRD_g72?#dyK>|`*e9%BGMO&nxHQqCOZg%!GK|C;f z&;NJM{hWL6?E8bm2O0takQRWKp{67SSZ}KNeV*F~VQ7WJ>Y`S;mA-f>r2g5yaOryM z#&=qIF;f0#_sovxW-)R4#+Y{MIC@K(@rPEqdI5cPaUBb-vNpDqPN&bvCowWqxtlmL zRJlE(-Z-rOq?LaQiAKNG%1e>*-Q6>B?bO#;ak%n}*T9Lz>7Uyd%81ezS3>DTWf0WM zT6sBCSsfcsyp<SVQZI|-;mS?Qzpx=}`bwgGVbH5`{d=suolWpSLp=yE4WasY6VuI1 zZ!it_*Vhj)4HGQRbOX~trW=`VV!D~>7N++ehdMjU^ev|MeNdMUS5DSzW42zWm>y>O zL#Fv&<*y@H-#j!j?c3H~q?yHAm<}<0AJY-0cQE}B(+@NK2-6)*16xnN-*T2^gFWp% zx(itwO7Xp*7j2ld--fJdni!-~B&19OQUyJk%6Ubxs){b;FqqYkf-&v5;21g-u@&9b zb5o$Bo3%^gd8Y?#+fJskj^R0u>4>C!-nMis)4FRyZ)ZH&1-dtqT+Wkma0<E&sg#ko zVMZ?`MI&B)A#;|Y=e&Ld^h&pw#5UrPw<fc$Spvf<xJ4@m*<wluQz+BLW)<}f98x!o zJ((K!R_;MszW@YEk+zo58QL(93yzPp0df-INE!Fgv*LMC21V(Tzdsx~-7pyL_&7Ka zj-G7n568+)2g02I2mAY<kH!wZHnJ<)rF1LtXuPwtTkTZ$MPrA|R8-U5D&Nziq>Zj! z0E%-g@9LAHbc@w8BfX-T(-kpI7ZlG;iB$(>(s6{gP+%QZQ7LR@lIfzJH$f>_uBnJI z>~UP(m;%MH@_Dl$$L^cX|8}FI1}+^xAAN$MEmw=pUw#fA6A^y)Fge;<t+H4xpiPw6 zk345`|35>VpXVsO&o9}*kBWT1Jtf3e{a>U0LlngRu@BzAN{siv%7Ms3<9+oAhC_{G z&}XuK8_h$8q?F|=B7i=dCS0SAGO{9-a{Lkx3WtvgW9#kJ46%bf!};T#C+o}gKO&eM zi<d}08oxk^^W4;b)W`Mp8?TS|=vGU8jQY>}D+{03N6ps`t7PZ`&rpR21jA1*pFgH; z)&DAepI;Yozw!S4F~5&_jqv-1pGiM>&n&8u;Fcr7@ooJwg7=(SpmSE6k^lSzy&WJ& zQ7ePMzjksw{7(wNqaw7^H~s-?=4ny#y8%PE|1e*!qr9_T+9qGyhC?oww2PK&DTZ)8 zFIf<Gqq7-E7UH`2_}Im4!A;kw9xs5nRSn%S@so7abd%F+4?H3ILBngJwpsu&kJh-{ zFWtm(MeuF3&_;NHIPO7g>u>xQDooB(wlCXx*O!lbQs#d|T;`L6&wcq}U%qS)hvDBH z@?1+UV{%@S%QxOj?MqJA255}$Bt6Qw{GXFy9QO%YsakkD2FD`TeU@>&$I<4h#b!S4 zAGG;uvB~B8IM29zA1jRSC%wk_5b1TsUnPBq@k69TxM0HMy1iMI(82f^@fhP1#CsV} z6W17@B0kEvMO@x<7{+y$`>(|E-=q9l#!nERWBfGndB#5>exC8O#8(*ql=vFspAlbY z{2cK+jDJBqM3+8>alT&>?_m5J;xWdTiT5)8196S<ABm4LewBEJ@n4CT7{5t;mhs!f a=NSKs_&nncv@TJKqofp%^f4~)-+uw%{cCdo literal 0 HcmV?d00001 diff --git a/lib/ll_types.c b/lib/ll_types.c new file mode 100644 index 0000000..5b0f106 --- /dev/null +++ b/lib/ll_types.c @@ -0,0 +1,134 @@ +/* + * ll_types.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/sockios.h> + +#include "rt_names.h" + +const char * ll_type_n2a(int type, char *buf, int len) +{ +#define __PF(f,n) { ARPHRD_##f, #n }, +static struct { + int type; + const char *name; +} arphrd_names[] = { +{ 0, "generic" }, +__PF(ETHER,ether) +__PF(EETHER,eether) +__PF(AX25,ax25) +__PF(PRONET,pronet) +__PF(CHAOS,chaos) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802,ieee802) +#else +__PF(IEEE802,tr) +#endif +__PF(ARCNET,arcnet) +__PF(APPLETLK,atalk) +__PF(DLCI,dlci) +#ifdef ARPHRD_ATM +__PF(ATM,atm) +#endif +__PF(METRICOM,metricom) +#ifdef ARPHRD_IEEE1394 +__PF(IEEE1394,ieee1394) +#endif +#ifdef ARPHRD_INFINIBAND +__PF(INFINIBAND,infiniband) +#endif + +__PF(SLIP,slip) +__PF(CSLIP,cslip) +__PF(SLIP6,slip6) +__PF(CSLIP6,cslip6) +__PF(RSRVD,rsrvd) +__PF(ADAPT,adapt) +__PF(ROSE,rose) +__PF(X25,x25) +#ifdef ARPHRD_HWX25 +__PF(HWX25,hwx25) +#endif +__PF(PPP,ppp) +__PF(HDLC,hdlc) +__PF(LAPB,lapb) +#ifdef ARPHRD_DDCMP +__PF(DDCMP,ddcmp) +#endif +#ifdef ARPHRD_RAWHDLC +__PF(RAWHDLC,rawhdlc) +#endif + +__PF(TUNNEL,ipip) +__PF(TUNNEL6,tunnel6) +__PF(FRAD,frad) +__PF(SKIP,skip) +__PF(LOOPBACK,loopback) +__PF(LOCALTLK,ltalk) +__PF(FDDI,fddi) +__PF(BIF,bif) +__PF(SIT,sit) +__PF(IPDDP,ip/ddp) +__PF(IPGRE,gre) +__PF(PIMREG,pimreg) +__PF(HIPPI,hippi) +__PF(ASH,ash) +__PF(ECONET,econet) +__PF(IRDA,irda) +__PF(FCPP,fcpp) +__PF(FCAL,fcal) +__PF(FCPL,fcpl) +__PF(FCFABRIC,fcfb0) +__PF(FCFABRIC+1,fcfb1) +__PF(FCFABRIC+2,fcfb2) +__PF(FCFABRIC+3,fcfb3) +__PF(FCFABRIC+4,fcfb4) +__PF(FCFABRIC+5,fcfb5) +__PF(FCFABRIC+6,fcfb6) +__PF(FCFABRIC+7,fcfb7) +__PF(FCFABRIC+8,fcfb8) +__PF(FCFABRIC+9,fcfb9) +__PF(FCFABRIC+10,fcfb10) +__PF(FCFABRIC+11,fcfb11) +__PF(FCFABRIC+12,fcfb12) +#ifdef ARPHRD_IEEE802_TR +__PF(IEEE802_TR,tr) +#endif +#ifdef ARPHRD_IEEE80211 +__PF(IEEE80211,ieee802.11) +#endif +#ifdef ARPHRD_VOID +__PF(VOID,void) +#endif +}; +#undef __PF + + int i; + for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) { + if (arphrd_names[i].type == type) + return arphrd_names[i].name; + } + snprintf(buf, len, "[%d]", type); + return buf; +} diff --git a/lib/ll_types.o b/lib/ll_types.o new file mode 100644 index 0000000000000000000000000000000000000000..11e3790300ea95649780e1f31f0dd9b16600257c GIT binary patch literal 4592 zcmbuCU1%It6vxjdX__`|n)EaFL$`vuMLONtr0KS(q?ERcls;I+V3jz#Gus)uAH&SF z-Qt6V`j9F}!IwOVPZIFammmn0f<*8^AB6SEJopeOK2#A}D8zea&S|#)1{A#P-h2MP zd(Zti_s-ni4^N*v)0fMUrX0OSYf33mcW}*~PR(f=p<%jU|4_O0mwo5nSiOGRd{)1! z+`Kb-^Wm%4#*Qy?yDNWOtbB5Vt-F=lpSK=<!)E2yZ}y#)%<5je%f^39IJZ9V>jymm zdIRX5Wu-M6p?(PTHqbjj?*Tmy8iU&t`m|*qEU+K+0O%pmBcQi|9tBMhz#QlS&>KJx zf+pC29OypK{h$XxZvZ_AdL!r|(3?PS20aXV3+Sz&M?h}_JqmgZ^mfoYK<@;-3-oT# zdq9taeg^bj(EC8|2Ymqav!I^?od<mo^y=!955y&}<wZe_yu|k+^0LC|l#WsuwOd|7 zHQ#B+6nLIjE|$oNY7CvkX<R0^Q45UOq^6gcKHAN+nLTlQlHx`XQZ3Cno677disI<1 zOOES=2}SMLBOb}WmW~&O<eM(3;e=Ik-C8rG$hnrHAT-@2S6VHvF-7&raVfrRNTb~j zt4{4QHPZRiT{oa=P^UOZC<u?ZZb+9RkHVlCd6&o!!qCh&_Q|WIOA8{`p?b}XTdz3{ zwm7R-i)n7Bc`3~&(tI+_kEXeqhG}^$&C6+iJk4!!&b;jsC6NZtvTeHB4qUqUg8QEC z!cH-Al<SOc**J7t-Vgf?=dY&IVw@Zqy_r7pXHT6vkuRJ*|JI@Wgf(fE@}**N(k|M^ z@`X1%H(zm*j31t|>a~eOL{@ygnK)HbCQ(-UYDHedu@bM7kd>OQ*<Z4%acp#(1^d{R zE!CbsYxbb&k=1G^o@M56I8JyQEOQ2$UMtZ_|Bo_nd^r?&KTI3ON#l_zqER)Z+-FL> zd$?+0IBW2JR?rqx@;G|W=za0BaCm&^3&S}rLq$HNYuSs&TyOj#9?+zp<RWCrUE%n) zFc^kztk<b&i+IW~URTL{dz1QKmvg3dDz$vwRIrWLi*86+9i`OyE9U*dIdNTgENh{0 z=JoTPul~y4G)(7`d1QGR4}?@=TlZi5IljFK{dt-gdu|`d_|0BtW1~Oor($Q=%;wB7 zFAiIcWQ6PAWY~J+cjWvQvu+;eIR4`bFu2_S-G^HsYt;YLDwMA`6P}+OVi==e4@nQt zp0l-nme)+v5B}=YaXAW4r~WizPjY_VxA)AS&mX6k%opDcX+@(kn_LgQ*s7UJE%YOI zwq;&vu~j6gnQfIEid$h6w30d<NS(<1;(l^pxPMN{bszP)<Gtl5C9*-UaATABY_QLx z)hwwWH|ve6J`c*ZUBR^Dq<kckq|c@DUBW9;D&H%52KYhIK5$)U2lx@ubHMri!)ZP% z9?w(PzX1HC=tbbVk4wPMi|zt{Tl5O>cSSz{UKOps<263M@NotvDQ{<z3c#-jF9ZKT zcm?<k;WNNL67B>4Sa=8cr^4ree<pk$xbDXS@Xw|FBJeMTF9H8bco+EB!dHOnzCHl{ zt+XGKmnB#8`Tt({IPf2Z7l8jPybSyo;T7P&3ZDVKEZhhFyYLS1d&1{{-xod){BPk4 zz#j=;1g`&2mw*pS9J|1Wg|7hD|D6wj@0RxZzfI%6U%39CQU1K}g1o<#7lfCATf!^A zCxy=dKOx))J}tZh{EYB9;OB(T1HT}A0r)$@7lAv%mw>y%yTIGR^Rk~TCFCjj2dJDh AY5)KL literal 0 HcmV?d00001 diff --git a/lib/rt_names.c b/lib/rt_names.c new file mode 100644 index 0000000..03df086 --- /dev/null +++ b/lib/rt_names.c @@ -0,0 +1,397 @@ +/* + * rt_names.c rtnetlink names DB. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <asm/types.h> +#include <linux/rtnetlink.h> + +#include "rt_names.h" + +static void rtnl_tab_initialize(char *file, char **tab, int size) +{ + char buf[512]; + FILE *fp; + + fp = fopen(file, "r"); + if (!fp) + return; + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + int id; + char namebuf[512]; + + while (*p == ' ' || *p == '\t') + p++; + if (*p == '#' || *p == '\n' || *p == 0) + continue; + if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 && + sscanf(p, "0x%x %s #", &id, namebuf) != 2 && + sscanf(p, "%d %s\n", &id, namebuf) != 2 && + sscanf(p, "%d %s #", &id, namebuf) != 2) { + fprintf(stderr, "Database %s is corrupted at %s\n", + file, p); + return; + } + + if (id<0 || id>size) + continue; + + tab[id] = strdup(namebuf); + } + fclose(fp); +} + + +static char * rtnl_rtprot_tab[256] = { + [RTPROT_UNSPEC] = "none", + [RTPROT_REDIRECT] ="redirect", + [RTPROT_KERNEL] = "kernel", + [RTPROT_BOOT] = "boot", + [RTPROT_STATIC] = "static", + + [RTPROT_GATED] = "gated", + [RTPROT_RA] = "ra", + [RTPROT_MRT] = "mrt", + [RTPROT_ZEBRA] ="zebra", + [RTPROT_BIRD] = "bird", + [RTPROT_DNROUTED] = "dnrouted", +#ifdef RTPROT_XORP + [RTPROT_XORP] = "xorp", +#endif +}; + + + +static int rtnl_rtprot_init; + +static void rtnl_rtprot_initialize(void) +{ + rtnl_rtprot_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_protos", + rtnl_rtprot_tab, 256); +} + +char * rtnl_rtprot_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtprot_tab[id]) { + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + } + if (rtnl_rtprot_tab[id]) + return rtnl_rtprot_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtprot_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtprot_init) + rtnl_rtprot_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtprot_tab[i] && + strcmp(rtnl_rtprot_tab[i], arg) == 0) { + cache = rtnl_rtprot_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtscope_tab[256] = { + "global", +}; + +static int rtnl_rtscope_init; + +static void rtnl_rtscope_initialize(void) +{ + rtnl_rtscope_init = 1; + rtnl_rtscope_tab[255] = "nowhere"; + rtnl_rtscope_tab[254] = "host"; + rtnl_rtscope_tab[253] = "link"; + rtnl_rtscope_tab[200] = "site"; + rtnl_tab_initialize("/etc/iproute2/rt_scopes", + rtnl_rtscope_tab, 256); +} + +char * rtnl_rtscope_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtscope_tab[id]) { + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + } + if (rtnl_rtscope_tab[id]) + return rtnl_rtscope_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rtscope_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtscope_init) + rtnl_rtscope_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtscope_tab[i] && + strcmp(rtnl_rtscope_tab[i], arg) == 0) { + cache = rtnl_rtscope_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rtrealm_tab[256] = { + "unknown", +}; + +static int rtnl_rtrealm_init; + +static void rtnl_rtrealm_initialize(void) +{ + rtnl_rtrealm_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_realms", + rtnl_rtrealm_tab, 256); +} + +char * rtnl_rtrealm_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtrealm_tab[id]) { + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + } + if (rtnl_rtrealm_tab[id]) + return rtnl_rtrealm_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + + +int rtnl_rtrealm_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtrealm_init) + rtnl_rtrealm_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtrealm_tab[i] && + strcmp(rtnl_rtrealm_tab[i], arg) == 0) { + cache = rtnl_rtrealm_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 0); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + + + +static char * rtnl_rttable_tab[256] = { + "unspec", +}; + +static int rtnl_rttable_init; + +static void rtnl_rttable_initialize(void) +{ + rtnl_rttable_init = 1; + rtnl_rttable_tab[255] = "local"; + rtnl_rttable_tab[254] = "main"; + rtnl_rttable_tab[253] = "default"; + rtnl_tab_initialize("/etc/iproute2/rt_tables", + rtnl_rttable_tab, 256); +} + +char * rtnl_rttable_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rttable_tab[id]) { + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + } + if (rtnl_rttable_tab[id]) + return rtnl_rttable_tab[id]; + snprintf(buf, len, "%d", id); + return buf; +} + +int rtnl_rttable_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rttable_init) + rtnl_rttable_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rttable_tab[i] && + strcmp(rtnl_rttable_tab[i], arg) == 0) { + cache = rtnl_rttable_tab[i]; + res = i; + *id = res; + return 0; + } + } + + i = strtoul(arg, &end, 0); + if (!end || end == arg || *end || i > 255) + return -1; + *id = i; + return 0; +} + + +static char * rtnl_rtdsfield_tab[256] = { + "0", +}; + +static int rtnl_rtdsfield_init; + +static void rtnl_rtdsfield_initialize(void) +{ + rtnl_rtdsfield_init = 1; + rtnl_tab_initialize("/etc/iproute2/rt_dsfield", + rtnl_rtdsfield_tab, 256); +} + +char * rtnl_dsfield_n2a(int id, char *buf, int len) +{ + if (id<0 || id>=256) { + snprintf(buf, len, "%d", id); + return buf; + } + if (!rtnl_rtdsfield_tab[id]) { + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + } + if (rtnl_rtdsfield_tab[id]) + return rtnl_rtdsfield_tab[id]; + snprintf(buf, len, "0x%02x", id); + return buf; +} + + +int rtnl_dsfield_a2n(__u32 *id, char *arg) +{ + static char *cache = NULL; + static unsigned long res; + char *end; + int i; + + if (cache && strcmp(cache, arg) == 0) { + *id = res; + return 0; + } + + if (!rtnl_rtdsfield_init) + rtnl_rtdsfield_initialize(); + + for (i=0; i<256; i++) { + if (rtnl_rtdsfield_tab[i] && + strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { + cache = rtnl_rtdsfield_tab[i]; + res = i; + *id = res; + return 0; + } + } + + res = strtoul(arg, &end, 16); + if (!end || end == arg || *end || res > 255) + return -1; + *id = res; + return 0; +} + diff --git a/lib/rt_names.o b/lib/rt_names.o new file mode 100644 index 0000000000000000000000000000000000000000..acab8ccbe695e054f14b1e3329e57ee38c873541 GIT binary patch literal 21104 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 literal 0 HcmV?d00001 diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..73ce865 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,558 @@ +/* + * utils.c + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * + * Changes: + * + * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <asm/types.h> +#include <linux/pkt_sched.h> + +#include "utils.h" + +int get_integer(int *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) + return -1; + *val = res; + return 0; +} + +int get_unsigned(unsigned *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > UINT_MAX) + return -1; + *val = res; + return 0; +} + +int get_u64(__u64 *val, const char *arg, int base) +{ + unsigned long long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoull(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res == 0xFFFFFFFFULL) + return -1; + *val = res; + return 0; +} + +int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +int get_u16(__u16 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFF) + return -1; + *val = res; + return 0; +} + +int get_u8(__u8 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFF) + return -1; + *val = res; + return 0; +} + +int get_s16(__s16 *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000) + return -1; + *val = res; + return 0; +} + +int get_s8(__s8 *val, const char *arg, int base) +{ + long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtol(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80) + return -1; + *val = res; + return 0; +} + +int get_addr_1(inet_prefix *addr, const char *name, int family) +{ + const char *cp; + unsigned char *ap = (unsigned char*)addr->data; + int i; + + memset(addr, 0, sizeof(*addr)); + + if (strcmp(name, "default") == 0 || + strcmp(name, "all") == 0 || + strcmp(name, "any") == 0) { + if (family == AF_DECnet) + return -1; + addr->family = family; + addr->bytelen = (family == AF_INET6 ? 16 : 4); + addr->bitlen = -1; + return 0; + } + + if (strchr(name, ':')) { + addr->family = AF_INET6; + if (family != AF_UNSPEC && family != AF_INET6) + return -1; + if (inet_pton(AF_INET6, name, addr->data) <= 0) + return -1; + addr->bytelen = 16; + addr->bitlen = -1; + return 0; + } + + if (family == AF_DECnet) { + struct dn_naddr dna; + addr->family = AF_DECnet; + if (dnet_pton(AF_DECnet, name, &dna) <= 0) + return -1; + memcpy(addr->data, dna.a_addr, 2); + addr->bytelen = 2; + addr->bitlen = -1; + return 0; + } + + addr->family = AF_INET; + if (family != AF_UNSPEC && family != AF_INET) + return -1; + addr->bytelen = 4; + addr->bitlen = -1; + for (cp=name, i=0; *cp; cp++) { + if (*cp <= '9' && *cp >= '0') { + ap[i] = 10*ap[i] + (*cp-'0'); + continue; + } + if (*cp == '.' && ++i <= 3) + continue; + return -1; + } + return 0; +} + +int get_prefix_1(inet_prefix *dst, char *arg, int family) +{ + int err; + unsigned plen; + char *slash; + + memset(dst, 0, sizeof(*dst)); + + if (strcmp(arg, "default") == 0 || + strcmp(arg, "any") == 0 || + strcmp(arg, "all") == 0) { + if (family == AF_DECnet) + return -1; + dst->family = family; + dst->bytelen = 0; + dst->bitlen = 0; + return 0; + } + + slash = strchr(arg, '/'); + if (slash) + *slash = 0; + + err = get_addr_1(dst, arg, family); + if (err == 0) { + switch(dst->family) { + case AF_INET6: + dst->bitlen = 128; + break; + case AF_DECnet: + dst->bitlen = 16; + break; + default: + case AF_INET: + dst->bitlen = 32; + } + if (slash) { + if (get_integer(&plen, slash+1, 0) || plen > dst->bitlen) { + err = -1; + goto done; + } + dst->bitlen = plen; + } + } +done: + if (slash) + *slash = '/'; + return err; +} + +int get_addr(inet_prefix *dst, const char *arg, int family) +{ + if (family == AF_PACKET) { + fprintf(stderr, "Error: \"%s\" may be inet address, but it is not allowed in this context.\n", arg); + exit(1); + } + if (get_addr_1(dst, arg, family)) { + fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", arg); + exit(1); + } + return 0; +} + +int get_prefix(inet_prefix *dst, char *arg, int family) +{ + if (family == AF_PACKET) { + fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg); + exit(1); + } + if (get_prefix_1(dst, arg, family)) { + fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", arg); + exit(1); + } + return 0; +} + +__u32 get_addr32(const char *name) +{ + inet_prefix addr; + if (get_addr_1(&addr, name, AF_INET)) { + fprintf(stderr, "Error: an IP address is expected rather than \"%s\"\n", name); + exit(1); + } + return addr.data[0]; +} + +void incomplete_command(void) +{ + fprintf(stderr, "Command line is not complete. Try option \"help\"\n"); + exit(-1); +} + +void missarg(const char *key) +{ + fprintf(stderr, "Error: argument \"%s\" is required\n", key); + exit(-1); +} + +void invarg(const char *msg, const char *arg) +{ + fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg); + exit(-1); +} + +void duparg(const char *key, const char *arg) +{ + fprintf(stderr, "Error: duplicate \"%s\": \"%s\" is the second value.\n", key, arg); + exit(-1); +} + +void duparg2(const char *key, const char *arg) +{ + fprintf(stderr, "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", key, arg); + exit(-1); +} + +int matches(const char *cmd, const char *pattern) +{ + int len = strlen(cmd); + if (len > strlen(pattern)) + return -1; + return memcmp(pattern, cmd, len); +} + +int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits) +{ + __u32 *a1 = a->data; + __u32 *a2 = b->data; + int words = bits >> 0x05; + + bits &= 0x1f; + + if (words) + if (memcmp(a1, a2, words << 2)) + return -1; + + if (bits) { + __u32 w1, w2; + __u32 mask; + + w1 = a1[words]; + w2 = a2[words]; + + mask = htonl((0xffffffff) << (0x20 - bits)); + + if ((w1 ^ w2) & mask) + return 1; + } + + return 0; +} + +int __iproute2_hz_internal; + +int __get_hz(void) +{ + char name[1024]; + int hz = 0; + FILE *fp; + + if (getenv("HZ")) + return atoi(getenv("HZ")) ? : HZ; + + if (getenv("PROC_NET_PSCHED")) { + snprintf(name, sizeof(name)-1, "%s", getenv("PROC_NET_PSCHED")); + } else if (getenv("PROC_ROOT")) { + snprintf(name, sizeof(name)-1, "%s/net/psched", getenv("PROC_ROOT")); + } else { + strcpy(name, "/proc/net/psched"); + } + fp = fopen(name, "r"); + + if (fp) { + unsigned nom, denom; + if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) + if (nom == 1000000) + hz = denom; + fclose(fp); + } + if (hz) + return hz; + return HZ; +} + +int __iproute2_user_hz_internal; + +int __get_user_hz(void) +{ + return sysconf(_SC_CLK_TCK); +} + +const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen) +{ + switch (af) { + case AF_INET: + case AF_INET6: + return inet_ntop(af, addr, buf, buflen); + case AF_IPX: + return ipx_ntop(af, addr, buf, buflen); + case AF_DECnet: + { + struct dn_naddr dna = { 2, { 0, 0, }}; + memcpy(dna.a_addr, addr, 2); + return dnet_ntop(af, &dna, buf, buflen); + } + default: + return "???"; + } +} + +#ifdef RESOLVE_HOSTNAMES +struct namerec +{ + struct namerec *next; + inet_prefix addr; + char *name; +}; + +static struct namerec *nht[256]; + +char *resolve_address(const char *addr, int len, int af) +{ + struct namerec *n; + struct hostent *h_ent; + unsigned hash; + static int notfirst; + + + if (af == AF_INET6 && ((__u32*)addr)[0] == 0 && + ((__u32*)addr)[1] == 0 && ((__u32*)addr)[2] == htonl(0xffff)) { + af = AF_INET; + addr += 12; + len = 4; + } + + hash = addr[len-1] ^ addr[len-2] ^ addr[len-3] ^ addr[len-4]; + + for (n = nht[hash]; n; n = n->next) { + if (n->addr.family == af && + n->addr.bytelen == len && + memcmp(n->addr.data, addr, len) == 0) + return n->name; + } + if ((n = malloc(sizeof(*n))) == NULL) + return NULL; + n->addr.family = af; + n->addr.bytelen = len; + n->name = NULL; + memcpy(n->addr.data, addr, len); + n->next = nht[hash]; + nht[hash] = n; + if (++notfirst == 1) + sethostent(1); + fflush(stdout); + + if ((h_ent = gethostbyaddr(addr, len, af)) != NULL) + n->name = strdup(h_ent->h_name); + + /* Even if we fail, "negative" entry is remembered. */ + return n->name; +} +#endif + + +const char *format_host(int af, int len, const void *addr, + char *buf, int buflen) +{ +#ifdef RESOLVE_HOSTNAMES + if (resolve_hosts) { + char *n; + if (len <= 0) { + switch (af) { + case AF_INET: + len = 4; + break; + case AF_INET6: + len = 16; + break; + case AF_IPX: + len = 10; + break; +#ifdef AF_DECnet + /* I see no reasons why gethostbyname + may not work for DECnet */ + case AF_DECnet: + len = 2; + break; +#endif + default: ; + } + } + if (len > 0 && + (n = resolve_address(addr, len, af)) != NULL) + return n; + } +#endif + return rt_addr_n2a(af, len, addr, buf, buflen); +} + + +__u8* hexstring_n2a(const __u8 *str, int len, __u8 *buf, int blen) +{ + __u8 *ptr = buf; + int i; + + for (i=0; i<len; i++) { + if (blen < 3) + break; + sprintf(ptr, "%02x", str[i]); + ptr += 2; + blen -= 2; + if (i != len-1 && blen > 1) { + *ptr++ = ':'; + blen--; + } + } + return buf; +} + +__u8* hexstring_a2n(const __u8 *str, __u8 *buf, int blen) +{ + int cnt = 0; + + for (;;) { + unsigned acc; + char ch; + + acc = 0; + + while ((ch = *str) != ':' && ch != 0) { + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'a' && ch <= 'f') + ch -= 'a'-10; + else if (ch >= 'A' && ch <= 'F') + ch -= 'A'-10; + else + return NULL; + acc = (acc<<4) + ch; + str++; + } + + if (acc > 255) + return NULL; + if (cnt < blen) { + buf[cnt] = acc; + cnt++; + } + if (ch == 0) + break; + ++str; + } + if (cnt < blen) + memset(buf+cnt, 0, blen-cnt); + return buf; +} diff --git a/lib/utils.o b/lib/utils.o new file mode 100644 index 0000000000000000000000000000000000000000..3fd2741966f98503d4699ecc58a0a73193eb5072 GIT binary patch literal 12208 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 literal 0 HcmV?d00001 diff --git a/man/man3/libnetlink.3 b/man/man3/libnetlink.3 new file mode 100644 index 0000000..145f38d --- /dev/null +++ b/man/man3/libnetlink.3 @@ -0,0 +1,197 @@ +.TH libnetlink 3 +.SH NAME +libnetlink \- A library for accessing the netlink service +.SH SYNOPSIS +.nf +#include <asm/types.h> +.br +#include <libnetlink.h> +.br +#include <linux/netlink.h> +.br +#include <linux/rtnetlink.h> +.sp +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +.sp +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +.sp +int rtnl_send(struct rtnl_handle *rth, char *buf, int len) +.sp +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +.sp +int rtnl_dump_filter(struct rtnl_handle *rth, + int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), + void *arg1, + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *arg2) +.sp +int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, +.br + int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), +.br + void *jarg) +.sp +int rtnl_listen(struct rtnl_handle *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +.sp +int rtnl_from_file(FILE *rtnl, + int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), + void *jarg) +.sp +int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) +.sp +int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) +.sp +int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) +.sp +int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) +.SH DESCRIPTION +libnetlink provides a higher level interface to +.BR rtnetlink(7). +The read functions return 0 on success and a negative errno on failure. +The send functions return the amount of data sent, or -1 on error. +.TP +rtnl_open +Open a rtnetlink socket and save the state into the +.B rth +handle. This handle is passed to all subsequent calls. +.B subscriptions +is a bitmap of the rtnetlink multicast groups the socket will be +a member of. + +.TP +rtnl_wilddump_request +Request a full dump of the +.B type +database for +.B family +addresses. +.B type +is a rtnetlink message type. +.\" XXX + +.TP +rtnl_dump_request +Request a full dump of the +.B type +data buffer into +.B buf +with maximum length of +.B len. +.B type +is a rtnetlink message type. + +.TP +rtnl_dump_filter +Receive netlink data after a request and filter it. +The +.B filter +callback checks if the received message is wanted. It gets the source +address of the message, the message itself and +.B arg1 +as arguments. 0 as return means that the filter passed, a negative +value is returned +by +.I rtnl_dump_filter +in case of error. NULL for +.I filter +means to not use a filter. +.B junk +is used to filter messages not destined to the local socket. +Only one message bundle is received. Unless there is no message +pending, this function does not block. + +.TP +rtnl_listen +Receive netlink data after a request and pass it to +.I handler. +.B handler +is a callback that gets the message source address, the message itself, +and the +.B jarg +cookie as arguments. It will get called for all received messages. +Only one message bundle is received. Unless there is no message +pending this function does not block. + +.TP +rtnl_from_file +Works like +.I rtnl_listen, +but reads a netlink message bundle from the file +.B file +and passes the messages to +.B handler +for parsing. The file contains raw data as received from a rtnetlink socket. +.PP +The following functions are useful to construct custom rtnetlink messages. For +simple database dumping with filtering it is better to use the higher level +functions above. See +.BR rtnetlink(3) +and +.BR netlink(3) +on how to generate a rtnetlink message. The following utility functions +require a continuous buffer that already contains a netlink message header +and a rtnetlink request. + +.TP +rtnl_send +Send the rtnetlink message in +.B buf +of length +.B len +to handle +.B rth. + +.TP +addattr32 +Add a __u32 attribute of type +.B type +and with value +.B data +to netlink message +.B n, +which is part of a buffer of length +.B maxlen. + +.TP +addattr_l +Add a variable length attribute of type +.B type +and with value +.B data +and +.B alen +length to netlink message +.B n, +which is part of a buffer of length +.B maxlen. +.B data +is copied. + +.TP +rta_addattr32 +Initialize the rtnetlink attribute +.B rta +with a __u32 data value. + +.TP +rta_addattr32 +Initialize the rtnetlink attribute +.B rta +with a variable length data value. + +.SH BUGS +The functions sometimes use fprintf and exit when a fatal error occurs. +This library should be named librtnetlink. + +.SH AUTHORS +netlink/rtnetlink was designed and writen by Alexey Kuznetsov. +Andi Kleen wrote the man page. + +.SH SEE ALSO +.BR netlink(7), +.BR rtnetlink(7) +.br +/usr/include/linux/rtnetlink.h diff --git a/man/man8/ip.8 b/man/man8/ip.8 new file mode 100644 index 0000000..cca6d1c --- /dev/null +++ b/man/man8/ip.8 @@ -0,0 +1,1810 @@ +.TH IP 8 "17 January 2002" "iproute2" "Linux" +.SH NAME +ip \- show / manipulate routing, devices, policy routing and tunnels +.SH SYNOPSIS + +.ad l +.in +8 +.ti -8 +.B ip +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR link " | " addr " | " route " | " rule " | " neigh " | " tunnel " | "\ +maddr " | " mroute " | " monitor " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-s\fR[\fItatistics\fR] | +\fB\-r\fR[\fIesolve\fR] | +\fB\-f\fR[\fIamily\fR] { +.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " +\fB\-o\fR[\fIneline\fR] } + +.ti -8 +.BI "ip link set " DEVICE +.RB "{ " up " | " down " | " arp " { " on " | " off " } |" +.br +.BR promisc " { " on " | " off " } |" +.br +.BR allmulti " { " on " | " off " } |" +.br +.BR dynamic " { " on " | " off " } |" +.br +.BR multicast " { " on " | " off " } |" +.br +.B txqueuelen +.IR PACKETS " |" +.br +.B name +.IR NEWNAME " |" +.br +.B address +.IR LLADDR " |" +.B broadcast +.IR LLADDR " |" +.br +.B mtu +.IR MTU " }" + +.ti -8 +.B ip link show +.RI "[ " DEVICE " ]" + +.ti -8 +.BR "ip addr" " { " add " | " del " } " +.IB IFADDR " dev " STRING + +.ti -8 +.BR "ip addr" " { " show " | " flush " } [ " dev +.IR STRING " ] [ " +.B scope +.IR SCOPE-ID " ] [ " +.B to +.IR PREFIX " ] [ " FLAG-LIST " ] [ " +.B label +.IR PATTERN " ]" + +.ti -8 +.IR IFADDR " := " PREFIX " | " ADDR +.B peer +.IR PREFIX " [ " +.B broadcast +.IR ADDR " ] [ " +.B anycast +.IR ADDR " ] [ " +.B label +.IR STRING " ] [ " +.B scope +.IR SCOPE-ID " ]" + +.ti -8 +.IR SCOPE-ID " := " +.RB "[ " host " | " link " | " global " | " +.IR NUMBER " ]" + +.ti -8 +.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG + +.ti -8 +.IR FLAG " := " +.RB "[ " permanent " | " dynamic " | " secondary " | " primary " | "\ +tentative " | " deprecated " ]" + +.ti -8 +.BR "ip route" " { " +.BR list " | " flush " } " +.I SELECTOR + +.ti -8 +.B ip route get +.IR ADDRESS " [ " +.BI from " ADDRESS " iif " STRING" +.RB " ] [ " oif +.IR STRING " ] [ " +.B tos +.IR TOS " ]" + +.ti -8 +.BR "ip route" " { " add " | " del " | " change " | " append " | "\ +replace " | " monitor " } " +.I ROUTE + +.ti -8 +.IR SELECTOR " := " +.RB "[ " root +.IR PREFIX " ] [ " +.B match +.IR PREFIX " ] [ " +.B exact +.IR PREFIX " ] [ " +.B table +.IR TABLE_ID " ] [ " +.B proto +.IR RTPROTO " ] [ " +.B type +.IR TYPE " ] [ " +.B scope +.IR SCOPE " ]" + +.ti -8 +.IR ROUTE " := " NODE_SPEC " [ " INFO_SPEC " ]" + +.ti -8 +.IR NODE_SPEC " := [ " TYPE " ] " PREFIX " [" +.B tos +.IR TOS " ] [ " +.B table +.IR TABLE_ID " ] [ " +.B proto +.IR RTPROTO " ] [ " +.B scope +.IR SCOPE " ] [ " +.B metric +.IR METRIC " ]" + +.ti -8 +.IR INFO_SPEC " := " "NH OPTIONS FLAGS" " [" +.B nexthop +.IR NH " ] ..." + +.ti -8 +.IR NH " := [ " +.B via +.IR ADDRESS " ] [ " +.B dev +.IR STRING " ] [ " +.B weight +.IR NUMBER " ] " NHFLAGS + +.ti -8 +.IR OPTIONS " := " FLAGS " [ " +.B mtu +.IR NUMBER " ] [ " +.B advmss +.IR NUMBER " ] [ " +.B rtt +.IR NUMBER " ] [ " +.B rttvar +.IR NUMBER " ] [ " +.B window +.IR NUMBER " ] [ " +.B cwnd +.IR NUMBER " ] [ " +.B ssthresh +.IR REALM " ] [ " +.B realms +.IR REALM " ]" + +.ti -8 +.IR TYPE " := [ " +.BR unicast " | " local " | " broadcast " | " multicast " | "\ +throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]" + +.ti -8 +.IR TABLE_ID " := [ " +.BR local "| " main " | " default " | " all " |" +.IR NUMBER " ]" + +.ti -8 +.IR SCOPE " := [ " +.BR host " | " link " | " global " |" +.IR NUMBER " ]" + +.ti -8 +.IR FLAGS " := [ " +.BR equalize " ]" + +.ti -8 +.IR NHFLAGS " := [ " +.BR onlink " | " pervasive " ]" + +.ti -8 +.IR RTPROTO " := [ " +.BR kernel " | " boot " | " static " |" +.IR NUMBER " ]" + +.ti -8 +.B ip rule +.RB " [ " list " | " add " | " del " ]" +.I SELECTOR ACTION + +.ti -8 +.IR SELECTOR " := [ " +.B from +.IR PREFIX " ] [ " +.B to +.IR PREFIX " ] [ " +.B tos +.IR TOS " ] [ " +.B fwmark +.IR FWMARK " ] [ " +.B dev +.IR STRING " ] [ " +.B pref +.IR NUMBER " ]" + +.ti -8 +.IR ACTION " := [ " +.B table +.IR TABLE_ID " ] [ " +.B nat +.IR ADDRESS " ] [ " +.BR prohibit " | " reject " | " unreachable " ] [ " realms +.RI "[" SRCREALM "/]" DSTREALM " ]" + +.ti -8 +.IR TABLE_ID " := [ " +.BR local " | " main " | " default " |" +.IR NUMBER " ]" + +.ti -8 +.BR "ip neigh" " { " add " | " del " | " change " | " replace " } { " +.IR ADDR " [ " +.B lladdr +.IR LLADDR " ] [ " +.BR nud " { " permanent " | " noarp " | " stale " | " reachable " } ] | " proxy +.IR ADDR " } [ " +.B dev +.IR DEV " ]" + +.ti -8 +.BR "ip neigh" " { " show " | " flush " } [ " to +.IR PREFIX " ] [ " +.B dev +.IR DEV " ] [ " +.B nud +.IR STATE " ]" + +.ti -8 +.BR "ip tunnel" " { " add " | " change " | " del " | " show " }" +.RI "[ " NAME " ]" +.br +.RB "[ " mode " { " ipip " | " gre " | " sit " } ]" +.br +.RB "[ " remote +.IR ADDR " ] [ " +.B local +.IR ADDR " ]" +.br +.RB "[ [" i "|" o "]" seq " ] [ [" i "|" o "]" key +.IR KEY " ] [ " +.RB "[" i "|" o "]" csum " ] ]" +.br +.RB "[ " ttl +.IR TTL " ] [ " +.B tos +.IR TOS " ] [ " +.RB "[" no "]" pmtudisc " ]" +.br +.RB "[ " dev +.IR PHYS_DEV " ]" + +.ti -8 +.IR ADDR " := { " IP_ADDRESS " |" +.BR any " }" + +.ti -8 +.IR TOS " := { " NUMBER " |" +.BR inherit " }" + +.ti -8 +.IR TTL " := { " 1 ".." 255 " | " +.BR inherit " }" + +.ti -8 +.IR KEY " := { " DOTTED_QUAD " | " NUMBER " }" + +.ti -8 +.BR "ip maddr" " [ " add " | " del " ]" +.IB MULTIADDR " dev " STRING + +.ti -8 +.BR "ip maddr show" " [ " dev +.IR STRING " ]" + +.ti -8 +.BR "ip mroute show" " [" +.IR PREFIX " ] [ " +.B from +.IR PREFIX " ] [ " +.B iif +.IR DEVICE " ]" + +.ti -8 +.BR "ip monitor" " [ " all " |" +.IR LISTofOBJECTS " ]" +.in -8 +.ad b + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +print the version of the +.B ip +utility and exit. + +.TP +.BR "\-s" , " \-stats", " \-statistics" +output more information. If the option +appears twice or more, the amount of information increases. +As a rule, the information is statistics or some time values. + +.TP +.BR "\-f" , " \-family" +followed by protocol family identifier: +.BR "inet" , " inet6" +or +.B link +,enforce the protocol family to use. If the option is not present, +the protocol family is guessed from other arguments. If the rest +of the command line does not give enough information to guess the +family, +.B ip +falls back to the default one, usually +.B inet +or +.BR "any" . +.B link +is a special family identifier meaning that no networking protocol +is involved. + +.TP +.B \-4 +shortcut for +.BR "-family inet" . + +.TP +.B \-6 +shortcut for +.BR "\-family inet6" . + +.TP +.B \-0 +shortcut for +.BR "\-family link" . + +.TP +.BR "\-o" , " \-oneline" +output each record on a single line, replacing line feeds +with the +.B '\' +character. This is convenient when you want to count records +with +.BR wc (1) + or to +.BR grep (1) +the output. + +.TP +.BR "\-r" , " \-resolve" +use the system's name resolver to print DNS names instead of +host addresses. + +.SH IP - COMMAND SYNTAX + +.SS +.I OBJECT + +.TP +.B link +- network device. + +.TP +.B address +- protocol (IP or IPv6) address on a device. +.TP +.B neighbour +- ARP or NDISC cache entry. + +.TP +.B route +- routing table entry. + +.TP +.B rule +- rule in routing policy database. + +.TP +.B maddress +- multicast address. + +.TP +.B mroute +- multicast routing cache entry. + +.TP +.B tunnel +- tunnel over IP. + +.PP +The names of all objects may be written in full or +abbreviated form, f.e. +.B address +is abbreviated as +.B addr +or just +.B a. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.BR "add" , " delete" +and +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH ip link - network device configuration + +.B link +is a network device and the corresponding commands +display and change the state of devices. + +.SS ip link set - change device attributes + +.TP +.BI dev " NAME " (default) +.I NAME +specifies network device to operate on. + +.TP +.BR up " and " down +change the state of the device to +.B UP +or +.BR "DOWN" . + +.TP +.BR "arp on " or " arp off" +change the +.B NOARP +flag on the device. + +.TP +.BR "multicast on " or " multicast off" +change the +.B MULTICAST +flag on the device. + +.TP +.BR "dynamic on " or " dynamic off" +change the +.B DYNAMIC +flag on the device. + +.TP +.BI name " NAME" +change the name of the device. This operation is not +recommended if the device is running or has some addresses +already configured. + +.TP +.BI txqueuelen " NUMBER" +.TP +.BI txqlen " NUMBER" +change the transmit queue length of the device. + +.TP +.BI mtu " NUMBER" +change the +.I MTU +of the device. + +.TP +.BI address " LLADDRESS" +change the station address of the interface. + +.TP +.BI broadcast " LLADDRESS" +.TP +.BI brd " LLADDRESS" +.TP +.BI peer " LLADDRESS" +change the link layer broadcast address or the peer address when +the interface is +.IR "POINTOPOINT" . + +.PP +.B Warning: +If multiple parameter changes are requested, +.B ip +aborts immediately after any of the changes have failed. +This is the only case when +.B ip +can move the system to an unpredictable state. The solution +is to avoid changing several parameters with one +.B ip link set +call. + +.SS ip link show - display device attributes + +.TP +.BI dev " NAME " (default) +.I NAME +specifies the network device to show. +If this argument is omitted all devices are listed. + +.TP +.B up +only display running interfaces. + +.SH ip address - protocol address management. + +The +.B address +is a protocol (IP or IPv6) address attached +to a network device. Each device must have at least one address +to use the corresponding protocol. It is possible to have several +different addresses attached to one device. These addresses are not +discriminated, so that the term +.B alias +is not quite appropriate for them and we do not use it in this document. +.sp +The +.B ip addr +command displays addresses and their properties, adds new addresses +and deletes old ones. + +.SS ip address add - add new protocol address. + +.TP +.BI dev " NAME" +the name of the device to add the address to. + +.TP +.BI local " ADDRESS " (default) +the address of the interface. The format of the address depends +on the protocol. It is a dotted quad for IP and a sequence of +hexadecimal halfwords separated by colons for IPv6. The +.I ADDRESS +may be followed by a slash and a decimal number which encodes +the network prefix length. + +.TP +.BI peer " ADDRESS" +the address of the remote endpoint for pointopoint interfaces. +Again, the +.I ADDRESS +may be followed by a slash and a decimal number, encoding the network +prefix length. If a peer address is specified, the local address +cannot have a prefix length. The network prefix is associated +with the peer rather than with the local address. + +.TP +.BI broadcast " ADDRESS" +the broadcast address on the interface. +.sp +It is possible to use the special symbols +.B '+' +and +.B '-' +instead of the broadcast address. In this case, the broadcast address +is derived by setting/resetting the host bits of the interface prefix. + +.TP +.BI label " NAME" +Each address may be tagged with a label string. +In order to preserve compatibility with Linux-2.0 net aliases, +this string must coincide with the name of the device or must be prefixed +with the device name followed by colon. + +.TP +.BI scope " SCOPE_VALUE" +the scope of the area where this address is valid. +The available scopes are listed in file +.BR "/etc/iproute2/rt_scopes" . +Predefined scope values are: + +.in +8 +.B global +- the address is globally valid. +.sp +.B site +- (IPv6 only) the address is site local, i.e. it is +valid inside this site. +.sp +.B link +- the address is link local, i.e. it is valid only on this device. +.sp +.B host +- the address is valid only inside this host. +.in -8 + +.SS ip address delete - delete protocol address +.B Arguments: +coincide with the arguments of +.B ip addr add. +The device name is a required argument. The rest are optional. +If no arguments are given, the first address is deleted. + +.SS ip address show - look at protocol addresses + +.TP +.BI dev " NAME " (default) +name of device. + +.TP +.BI scope " SCOPE_VAL" +only list addresses with this scope. + +.TP +.BI to " PREFIX" +only list addresses matching this prefix. + +.TP +.BI label " PATTERN" +only list addresses with labels matching the +.IR "PATTERN" . +.I PATTERN +is a usual shell style pattern. + +.TP +.BR dynamic " and " permanent +(IPv6 only) only list addresses installed due to stateless +address configuration or only list permanent (not dynamic) +addresses. + +.TP +.B tentative +(IPv6 only) only list addresses which did not pass duplicate +address detection. + +.TP +.B deprecated +(IPv6 only) only list deprecated addresses. + +.TP +.BR primary " and " secondary +only list primary (or secondary) addresses. + +.SS ip address flush - flush protocol addresses +This command flushes the protocol addresses selected by some criteria. + +.PP +This command has the same arguments as +.B show. +The difference is that it does not run when no arguments are given. + +.PP +.B Warning: +This command (and other +.B flush +commands described below) is pretty dangerous. If you make a mistake, +it will not forgive it, but will cruelly purge all the addresses. + +.PP +With the +.B -statistics +option, the command becomes verbose. It prints out the number of deleted +addresses and the number of rounds made to flush the address list. If +this option is given twice, +.B ip addr flush +also dumps all the deleted addresses in the format described in the +previous subsection. + +.SH ip neighbour - neighbour/arp tables management. + +.B neighbour +objects establish bindings between protocol addresses and +link layer addresses for hosts sharing the same link. +Neighbour entries are organized into tables. The IPv4 neighbour table +is known by another name - the ARP table. + +.P +The corresponding commands display neighbour bindings +and their properties, add new neighbour entries and delete old ones. + +.SS ip neighbour add - add a new neighbour entry +.SS ip neighbour change - change an existing entry +.SS ip neighbour replace - add a new entry or change an existing one + +These commands create new neighbour records or update existing ones. + +.TP +.BI to " ADDRESS " (default) +the protocol address of the neighbour. It is either an IPv4 or IPv6 address. + +.TP +.BI dev " NAME" +the interface to which this neighbour is attached. + +.TP +.BI lladdr " LLADDRESS" +the link layer address of the neighbour. +.I LLADDRESS +can also be +.BR "null" . + +.TP +.BI nud " NUD_STATE" +the state of the neighbour entry. +.B nud +is an abbreviation for 'Neigh bour Unreachability Detection'. +The state can take one of the following values: + +.in +8 +.B permanent +- the neighbour entry is valid forever and can be only +be removed administratively. +.sp + +.B noarp +- the neighbour entry is valid. No attempts to validate +this entry will be made but it can be removed when its lifetime expires. +.sp + +.B reachable +- the neighbour entry is valid until the reachability +timeout expires. +.sp + +.B stale +- the neighbour entry is valid but suspicious. +This option to +.B ip neigh +does not change the neighbour state if it was valid and the address +is not changed by this command. +.in -8 + +.SS ip neighbour delete - delete a neighbour entry +This command invalidates a neighbour entry. + +.PP +The arguments are the same as with +.BR "ip neigh add" , +except that +.B lladdr +and +.B nud +are ignored. + +.PP +.B Warning: +Attempts to delete or manually change a +.B noarp +entry created by the kernel may result in unpredictable behaviour. +Particularly, the kernel may try to resolve this address even +on a +.B NOARP +interface or if the address is multicast or broadcast. + +.SS ip neighbour show - list neighbour entries + +This commands displays neighbour tables. + +.TP +.BI to " ADDRESS " (default) +the prefix selecting the neighbours to list. + +.TP +.BI dev " NAME" +only list the neighbours attached to this device. + +.TP +.B unused +only list neighbours which are not currently in use. + +.TP +.BI nud " NUD_STATE" +only list neighbour entries in this state. +.I NUD_STATE +takes values listed below or the special value +.B all +which means all states. This option may occur more than once. +If this option is absent, +.B ip +lists all entries except for +.B none +and +.BR "noarp" . + +.SS ip neighbour flush - flush neighbour entries +This command flushes neighbour tables, selecting +entries to flush by some criteria. + +.PP +This command has the same arguments as +.B show. +The differences are that it does not run when no arguments are given, +and that the default neighbour states to be flushed do not include +.B permanent +and +.BR "noarp" . + +.PP +With the +.B -statistics +option, the command becomes verbose. It prints out the number of +deleted neighbours and the number of rounds made to flush the +neighbour table. If the option is given +twice, +.B ip neigh flush +also dumps all the deleted neighbours. + +.SH ip route - routing table management +Manipulate route entries in the kernel routing tables keep +information about paths to other networked nodes. +.sp +.B Route types: + +.in +8 +.B unicast +- the route entry describes real paths to the destinations covered +by the route prefix. + +.sp +.B unreachable +- these destinations are unreachable. Packets are discarded and the +ICMP message +.I host unreachable +is generated. +The local senders get an +.I EHOSTUNREACH +error. + +.sp +.B blackhole +- these destinations are unreachable. Packets are discarded silently. +The local senders get an +.I EINVAL +error. + +.sp +.B prohibit +- these destinations are unreachable. Packets are discarded and the +ICMP message +.I communication administratively prohibited +is generated. The local senders get an +.I EACCES +error. + +.sp +.B local +- the destinations are assigned to this host. The packets are looped +back and delivered locally. + +.sp +.B broadcast +- the destinations are broadcast addresses. The packets are sent as +link broadcasts. + +.sp +.B throw +- a special control route used together with policy rules. If such a +route is selected, lookup in this table is terminated pretending that +no route was found. Without policy routing it is equivalent to the +absence of the route in the routing table. The packets are dropped +and the ICMP message +.I net unreachable +is generated. The local senders get an +.I ENETUNREACH +error. + +.sp +.B nat +- a special NAT route. Destinations covered by the prefix +are considered to be dummy (or external) addresses which require translation +to real (or internal) ones before forwarding. The addresses to translate to +are selected with the attribute +.B Warning: +Route NAT is no longer supported in Linux 2.6. + + +.BR "via" . +.sp +.B anycast +.RI "- " "not implemented" +the destinations are +.I anycast +addresses assigned to this host. They are mainly equivalent +to +.B local +with one difference: such addresses are invalid when used +as the source address of any packet. + +.sp +.B multicast +- a special type used for multicast routing. It is not present in +normal routing tables. +.in -8 + +.P +.B Route tables: +Linux-2.x can pack routes into several routing +tables identified by a number in the range from 1 to 255 or by +name from the file +.B /etc/iproute2/rt_tables +. By default all normal routes are inserted into the +.B main +table (ID 254) and the kernel only uses this table when calculating routes. + +.sp +Actually, one other table always exists, which is invisible but +even more important. It is the +.B local +table (ID 255). This table +consists of routes for local and broadcast addresses. The kernel maintains +this table automatically and the administrator usually need not modify it +or even look at it. + +The multiple routing tables enter the game when +.I policy routing +is used. + +.SS ip route add - add new route +.SS ip route change - change route +.SS ip route replace - change or add new one + +.TP +.BI to " TYPE PREFIX " (default) +the destination prefix of the route. If +.I TYPE +is omitted, +.B ip +assumes type +.BR "unicast" . +Other values of +.I TYPE +are listed above. +.I PREFIX +is an IP or IPv6 address optionally followed by a slash and the +prefix length. If the length of the prefix is missing, +.B ip +assumes a full-length host route. There is also a special +.I PREFIX +.B default +- which is equivalent to IP +.B 0/0 +or to IPv6 +.BR "::/0" . + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +the Type Of Service (TOS) key. This key has no associated mask and +the longest match is understood as: First, compare the TOS +of the route and of the packet. If they are not equal, then the packet +may still match a route with a zero TOS. +.I TOS +is either an 8 bit hexadecimal number or an identifier +from +.BR "/etc/iproute2/rt_dsfield" . + +.TP +.BI metric " NUMBER" +.TP +.BI preference " NUMBER" +the preference value of the route. +.I NUMBER +is an arbitrary 32bit number. + +.TP +.BI table " TABLEID" +the table to add this route to. +.I TABLEID +may be a number or a string from the file +.BR "/etc/iproute2/rt_tables" . +If this parameter is omitted, +.B ip +assumes the +.B main +table, with the exception of +.BR local " , " broadcast " and " nat +routes, which are put into the +.B local +table by default. + +.TP +.BI dev " NAME" +the output device name. + +.TP +.BI via " ADDRESS" +the address of the nexthop router. Actually, the sense of this field +depends on the route type. For normal +.B unicast +routes it is either the true next hop router or, if it is a direct +route installed in BSD compatibility mode, it can be a local address +of the interface. For NAT routes it is the first address of the block +of translated IP destinations. + +.TP +.BI src " ADDRESS" +the source address to prefer when sending to the destinations +covered by the route prefix. + +.TP +.BI realm " REALMID" +the realm to which this route is assigned. +.I REALMID +may be a number or a string from the file +.BR "/etc/iproute2/rt_realms" . + +.TP +.BI mtu " MTU" +.TP +.BI "mtu lock" " MTU" +the MTU along the path to the destination. If the modifier +.B lock +is not used, the MTU may be updated by the kernel due to +Path MTU Discovery. If the modifier +.B lock +is used, no path MTU discovery will be tried, all packets +will be sent without the DF bit in IPv4 case or fragmented +to MTU for IPv6. + +.TP +.BI window " NUMBER" +the maximal window for TCP to advertise to these destinations, +measured in bytes. It limits maximal data bursts that our TCP +peers are allowed to send to us. + +.TP +.BI rtt " NUMBER" +the initial RTT ('Round Trip Time') estimate. + +.TP +.BI rttvar " NUMBER " "(2.3.15+ only)" +the initial RTT variance estimate. + +.TP +.BI ssthresh " NUMBER " "(2.3.15+ only)" +an estimate for the initial slow start threshold. + +.TP +.BI cwnd " NUMBER " "(2.3.15+ only)" +the clamp for congestion window. It is ignored if the +.B lock +flag is not used. + +.TP +.BI advmss " NUMBER " "(2.3.15+ only)" +the MSS ('Maximal Segment Size') to advertise to these +destinations when establishing TCP connections. If it is not given, +Linux uses a default value calculated from the first hop device MTU. +(If the path to these destination is asymmetric, this guess may be wrong.) + +.TP +.BI reordering " NUMBER " "(2.3.15+ only)" +Maximal reordering on the path to this destination. +If it is not given, Linux uses the value selected with +.B sysctl +variable +.BR "net/ipv4/tcp_reordering" . + +.TP +.BI nexthop " NEXTHOP" +the nexthop of a multipath route. +.I NEXTHOP +is a complex value with its own syntax similar to the top level +argument lists: + +.in +8 +.BI via " ADDRESS" +- is the nexthop router. +.sp + +.BI dev " NAME" +- is the output device. +.sp + +.BI weight " NUMBER" +- is a weight for this element of a multipath +route reflecting its relative bandwidth or quality. +.in -8 + +.TP +.BI scope " SCOPE_VAL" +the scope of the destinations covered by the route prefix. +.I SCOPE_VAL +may be a number or a string from the file +.BR "/etc/iproute2/rt_scopes" . +If this parameter is omitted, +.B ip +assumes scope +.B global +for all gatewayed +.B unicast +routes, scope +.B link +for direct +.BR unicast " and " broadcast +routes and scope +.BR host " for " local +routes. + +.TP +.BI protocol " RTPROTO" +the routing protocol identifier of this route. +.I RTPROTO +may be a number or a string from the file +.BR "/etc/iproute2/rt_protos" . +If the routing protocol ID is not given, +.B ip assumes protocol +.B boot +(i.e. it assumes the route was added by someone who doesn't +understand what they are doing). Several protocol values have +a fixed interpretation. +Namely: + +.in +8 +.B redirect +- the route was installed due to an ICMP redirect. +.sp + +.B kernel +- the route was installed by the kernel during autoconfiguration. +.sp + +.B boot +- the route was installed during the bootup sequence. +If a routing daemon starts, it will purge all of them. +.sp + +.B static +- the route was installed by the administrator +to override dynamic routing. Routing daemon will respect them +and, probably, even advertise them to its peers. +.sp + +.B ra +- the route was installed by Router Discovery protocol. +.in -8 + +.sp +The rest of the values are not reserved and the administrator is free +to assign (or not to assign) protocol tags. + +.TP +.B onlink +pretend that the nexthop is directly attached to this link, +even if it does not match any interface prefix. + +.TP +.B equalize +allow packet by packet randomization on multipath routes. +Without this modifier, the route will be frozen to one selected +nexthop, so that load splitting will only occur on per-flow base. +.B equalize +only works if the kernel is patched. + +.SS ip route delete - delete route + +.B ip route del +has the same arguments as +.BR "ip route add" , +but their semantics are a bit different. + +Key values +.RB "(" to ", " tos ", " preference " and " table ")" +select the route to delete. If optional attributes are present, +.B ip +verifies that they coincide with the attributes of the route to delete. +If no route with the given key and attributes was found, +.B ip route del +fails. + +.SS ip route show - list routes +the command displays the contents of the routing tables or the route(s) +selected by some criteria. + +.TP +.BI to " SELECTOR " (default) +only select routes from the given range of destinations. +.I SELECTOR +consists of an optional modifier +.RB "(" root ", " match " or " exact ")" +and a prefix. +.BI root " PREFIX" +selects routes with prefixes not shorter than +.IR PREFIX "." +F.e. +.BI root " 0/0" +selects the entire routing table. +.BI match " PREFIX" +selects routes with prefixes not longer than +.IR PREFIX "." +F.e. +.BI match " 10.0/16" +selects +.IR 10.0/16 "," +.IR 10/8 " and " 0/0 , +but it does not select +.IR 10.1/16 " and " 10.0.0/24 . +And +.BI exact " PREFIX" +(or just +.IR PREFIX ")" +selects routes with this exact prefix. If neither of these options +are present, +.B ip +assumes +.BI root " 0/0" +i.e. it lists the entire table. + +.TP +.BI tos " TOS" +.BI dsfield " TOS" +only select routes with the given TOS. + +.TP +.BI table " TABLEID" +show the routes from this table(s). The default setting is to show +.BR table main "." +.I TABLEID +may either be the ID of a real table or one of the special values: +.sp +.in +8 +.B all +- list all of the tables. +.sp +.B cache +- dump the routing cache. +.in -8 + +.TP +.B cloned +.TP +.B cached +list cloned routes i.e. routes which were dynamically forked from +other routes because some route attribute (f.e. MTU) was updated. +Actually, it is equivalent to +.BR "table cache" "." + +.TP +.BI from " SELECTOR" +the same syntax as for +.BR to "," +but it binds the source address range rather than destinations. +Note that the +.B from +option only works with cloned routes. + +.TP +.BI protocol " RTPROTO" +only list routes of this protocol. + +.TP +.BI scope " SCOPE_VAL" +only list routes with this scope. + +.TP +.BI type " TYPE" +only list routes of this type. + +.TP +.BI dev " NAME" +only list routes going via this device. + +.TP +.BI via " PREFIX" +only list routes going via the nexthop routers selected by +.IR PREFIX "." + +.TP +.BI src " PREFIX" +only list routes with preferred source addresses selected +by +.IR PREFIX "." + +.TP +.BI realm " REALMID" +.TP +.BI realms " FROMREALM/TOREALM" +only list routes with these realms. + +.SS ip route flush - flush routing tables +this command flushes routes selected by some criteria. + +.sp +The arguments have the same syntax and semantics as the arguments of +.BR "ip route show" , +but routing tables are not listed but purged. The only difference is +the default action: +.B show +dumps all the IP main routing table but +.B flush +prints the helper page. + +.sp +With the +.B -statistics +option, the command becomes verbose. It prints out the number of +deleted routes and the number of rounds made to flush the routing +table. If the option is given +twice, +.B ip route flush +also dumps all the deleted routes in the format described in the +previous subsection. + +.SS ip route get - get a single route +this command gets a single route to a destination and prints its +contents exactly as the kernel sees it. + +.TP +.BI to " ADDRESS " (default) +the destination address. + +.TP +.BI from " ADDRESS" +the source address. + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +the Type Of Service. + +.TP +.BI iif " NAME" +the device from which this packet is expected to arrive. + +.TP +.BI oif " NAME" +force the output device on which this packet will be routed. + +.TP +.B connected +if no source address +.RB "(option " from ")" +was given, relookup the route with the source set to the preferred +address received from the first lookup. +If policy routing is used, it may be a different route. + +.P +Note that this operation is not equivalent to +.BR "ip route show" . +.B show +shows existing routes. +.B get +resolves them and creates new clones if necessary. Essentially, +.B get +is equivalent to sending a packet along this path. +If the +.B iif +argument is not given, the kernel creates a route +to output packets towards the requested destination. +This is equivalent to pinging the destination +with a subsequent +.BR "ip route ls cache" , +however, no packets are actually sent. With the +.B iif +argument, the kernel pretends that a packet arrived from this interface +and searches for a path to forward the packet. + +.SH ip rule - routing policy database management + +.BR "Rule" s +in the routing policy database control the route selection algorithm. + +.P +Classic routing algorithms used in the Internet make routing decisions +based only on the destination address of packets (and in theory, +but not in practice, on the TOS field). + +.P +In some circumstances we want to route packets differently depending not only +on destination addresses, but also on other packet fields: source address, +IP protocol, transport protocol ports or even packet payload. +This task is called 'policy routing'. + +.P +To solve this task, the conventional destination based routing table, ordered +according to the longest match rule, is replaced with a 'routing policy +database' (or RPDB), which selects routes by executing some set of rules. + +.P +Each policy routing rule consists of a +.B selector +and an +.B action predicate. +The RPDB is scanned in the order of increasing priority. The selector +of each rule is applied to {source address, destination address, incoming +interface, tos, fwmark} and, if the selector matches the packet, +the action is performed. The action predicate may return with success. +In this case, it will either give a route or failure indication +and the RPDB lookup is terminated. Otherwise, the RPDB program +continues on the next rule. + +.P +Semantically, natural action is to select the nexthop and the output device. + +.P +At startup time the kernel configures the default RPDB consisting of three +rules: + +.TP +1. +Priority: 0, Selector: match anything, Action: lookup routing +table +.B local +(ID 255). +The +.B local +table is a special routing table containing +high priority control routes for local and broadcast addresses. +.sp +Rule 0 is special. It cannot be deleted or overridden. + +.TP +2. +Priority: 32766, Selector: match anything, Action: lookup routing +table +.B main +(ID 254). +The +.B main +table is the normal routing table containing all non-policy +routes. This rule may be deleted and/or overridden with other +ones by the administrator. + +.TP +3. +Priority: 32767, Selector: match anything, Action: lookup routing +table +.B default +(ID 253). +The +.B default +table is empty. It is reserved for some post-processing if no previous +default rules selected the packet. +This rule may also be deleted. + +.P +Each RPDB entry has additional +attributes. F.e. each rule has a pointer to some routing +table. NAT and masquerading rules have an attribute to select new IP +address to translate/masquerade. Besides that, rules have some +optional attributes, which routes have, namely +.BR "realms" . +These values do not override those contained in the routing tables. They +are only used if the route did not select any attributes. + +.sp +The RPDB may contain rules of the following types: + +.in +8 +.B unicast +- the rule prescribes to return the route found +in the routing table referenced by the rule. + +.B blackhole +- the rule prescribes to silently drop the packet. + +.B unreachable +- the rule prescribes to generate a 'Network is unreachable' error. + +.B prohibit +- the rule prescribes to generate 'Communication is administratively +prohibited' error. + +.B nat +- the rule prescribes to translate the source address +of the IP packet into some other value. +.in -8 + +.SS ip rule add - insert a new rule +.SS ip rule delete - delete a rule + +.TP +.BI type " TYPE " (default) +the type of this rule. The list of valid types was given in the previous +subsection. + +.TP +.BI from " PREFIX" +select the source prefix to match. + +.TP +.BI to " PREFIX" +select the destination prefix to match. + +.TP +.BI iif " NAME" +select the incoming device to match. If the interface is loopback, +the rule only matches packets originating from this host. This means +that you may create separate routing tables for forwarded and local +packets and, hence, completely segregate them. + +.TP +.BI tos " TOS" +.TP +.BI dsfield " TOS" +select the TOS value to match. + +.TP +.BI fwmark " MARK" +select the +.B fwmark +value to match. + +.TP +.BI priority " PREFERENCE" +the priority of this rule. Each rule should have an explicitly +set +.I unique +priority value. + +.TP +.BI table " TABLEID" +the routing table identifier to lookup if the rule selector matches. + +.TP +.BI realms " FROM/TO" +Realms to select if the rule matched and the routing table lookup +succeeded. Realm +.I TO +is only used if the route did not select any realm. + +.TP +.BI nat " ADDRESS" +The base of the IP address block to translate (for source addresses). +The +.I ADDRESS +may be either the start of the block of NAT addresses (selected by NAT +routes) or a local host address (or even zero). +In the last case the router does not translate the packets, but +masquerades them to this address. + +.B Warning: +Changes to the RPDB made with these commands do not become active +immediately. It is assumed that after a script finishes a batch of +updates, it flushes the routing cache with +.BR "ip route flush cache" . + +.SS ip rule show - list rules +This command has no arguments. + +.SH ip maddress - multicast addresses management + +.B maddress +objects are multicast addresses. + +.SS ip maddress show - list multicast addresses + +.TP +.BI dev " NAME " (default) +the device name. + +.SS ip maddress add - add a multicast address +.SS ip maddress delete - delete a multicast address +these commands attach/detach a static link layer multicast address +to listen on the interface. +Note that it is impossible to join protocol multicast groups +statically. This command only manages link layer addresses. + +.TP +.BI address " LLADDRESS " (default) +the link layer multicast address. + +.TP +.BI dev " NAME" +the device to join/leave this multicast address. + +.SH ip mroute - multicast routing cache management +.B mroute +objects are multicast routing cache entries created by a user level +mrouting daemon (f.e. +.B pimd +or +.B mrouted +). + +Due to the limitations of the current interface to the multicast routing +engine, it is impossible to change +.B mroute +objects administratively, so we may only display them. This limitation +will be removed in the future. + +.SS ip mroute show - list mroute cache entries + +.TP +.BI to " PREFIX " (default) +the prefix selecting the destination multicast addresses to list. + +.TP +.BI iif " NAME" +the interface on which multicast packets are received. + +.TP +.BI from " PREFIX" +the prefix selecting the IP source addresses of the multicast route. + +.SH ip tunnel - tunnel configuration +.B tunnel +objects are tunnels, encapsulating packets in IPv4 packets and then +sending them over the IP infrastructure. + +.SS ip tunnel add - add a new tunnel +.SS ip tunnel change - change an existing tunnel +.SS ip tunnel delete - destroy a tunnel + +.TP +.BI name " NAME " (default) +select the tunnel device name. + +.TP +.BI mode " MODE" +set the tunnel mode. Three modes are currently available: +.BR ipip ", " sit " and " gre "." + +.TP +.BI remote " ADDRESS" +set the remote endpoint of the tunnel. + +.TP +.BI local " ADDRESS" +set the fixed local address for tunneled packets. +It must be an address on another interface of this host. + +.TP +.BI ttl " N" +set a fixed TTL +.I N +on tunneled packets. +.I N +is a number in the range 1--255. 0 is a special value +meaning that packets inherit the TTL value. +The default value is: +.BR "inherit" . + +.TP +.BI tos " T" +.TP +.BI dsfield " T" +set a fixed TOS +.I T +on tunneled packets. +The default value is: +.BR "inherit" . + +.TP +.BI dev " NAME" +bind the tunnel to the device +.I NAME +so that tunneled packets will only be routed via this device and will +not be able to escape to another device when the route to endpoint +changes. + +.TP +.B nopmtudisc +disable Path MTU Discovery on this tunnel. +It is enabled by default. Note that a fixed ttl is incompatible +with this option: tunnelling with a fixed ttl always makes pmtu +discovery. + +.TP +.BI key " K" +.TP +.BI ikey " K" +.TP +.BI okey " K" +.RB ( " only GRE tunnels " ) +use keyed GRE with key +.IR K ". " K +is either a number or an IP address-like dotted quad. +The +.B key +parameter sets the key to use in both directions. +The +.BR ikey " and " okey +parameters set different keys for input and output. + +.TP +.BR csum ", " icsum ", " ocsum +.RB ( " only GRE tunnels " ) +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" . + +.TP +.BR seq ", " iseq ", " oseq +.RB ( " only GRE tunnels " ) +serialize packets. +The +.B oseq +flag enables sequencing of outgoing packets. +The +.B iseq +flag requires that all input packets are serialized. +The +.B seq +flag is equivalent to the combination +.BR "iseq oseq" . +.B It isn't work. Don't use it. + +.SS ip tunnel show - list tunnels +This command has no arguments. + +.SH ip monitor and rtmon - state monitoring + +The +.B ip +utility can monitor the state of devices, addresses +and routes continuously. This option has a slightly different format. +Namely, the +.B monitor +command is the first in the command line and then the object list follows: + +.BR "ip monitor" " [ " all " |" +.IR LISTofOBJECTS " ]" + +.I OBJECT-LIST +is the list of object types that we want to monitor. +It may contain +.BR link ", " address " and " route "." +If no +.B file +argument is given, +.B ip +opens RTNETLINK, listens on it and dumps state changes in the format +described in previous sections. + +.P +If a file name is given, it does not listen on RTNETLINK, +but opens the file containing RTNETLINK messages saved in binary format +and dumps them. Such a history file can be generated with the +.B rtmon +utility. This utility has a command line syntax similar to +.BR "ip monitor" . +Ideally, +.B rtmon +should be started before the first network configuration command +is issued. F.e. if you insert: +.sp +.in +8 +rtmon file /var/log/rtmon.log +.in -8 +.sp +in a startup script, you will be able to view the full history +later. + +.P +Certainly, it is possible to start +.B rtmon +at any time. +It prepends the history with the state snapshot dumped at the moment +of starting. + +.SH HISTORY +.B ip +was written by Alexey N. Kuznetsov and added in Linux 2.2. +.SH SEE ALSO +.BR tc (8) +.br +.RB "IP Command reference " ip-cref.ps +.br +.RB "IP tunnels " ip-cref.ps + +.SH AUTHOR +Original Manpage by Michail Litvak <mci@owl.openwall.com> diff --git a/man/man8/tc-cbq-details.8 b/man/man8/tc-cbq-details.8 new file mode 100644 index 0000000..e47da62 --- /dev/null +++ b/man/man8/tc-cbq-details.8 @@ -0,0 +1,425 @@ +.TH CBQ 8 "8 December 2001" "iproute2" "Linux" +.SH NAME +CBQ \- Class Based Queueing +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] cbq avpkt +bytes +.B bandwidth +rate +.B [ cell +bytes +.B ] [ ewma +log +.B ] [ mpu +bytes +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] cbq allot +bytes +.B [ bandwidth +rate +.B ] [ rate +rate +.B ] prio +priority +.B [ weight +weight +.B ] [ minburst +packets +.B ] [ maxburst +packets +.B ] [ ewma +log +.B ] [ cell +bytes +.B ] avpkt +bytes +.B [ mpu +bytes +.B ] [ bounded isolated ] [ split +handle +.B & defmap +defmap +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +Class Based Queueing is a classful qdisc that implements a rich +linksharing hierarchy of classes. It contains shaping elements as +well as prioritizing capabilities. Shaping is performed using link +idle time calculations based on the timing of dequeue events and +underlying link bandwidth. + +.SH SHAPING ALGORITHM +Shaping is done using link idle time calculations, and actions taken if +these calculations deviate from set limits. + +When shaping a 10mbit/s connection to 1mbit/s, the link will +be idle 90% of the time. If it isn't, it needs to be throttled so that it +IS idle 90% of the time. + +From the kernel's perspective, this is hard to measure, so CBQ instead +derives the idle time from the number of microseconds (in fact, jiffies) +that elapse between requests from the device driver for more data. Combined +with the knowledge of packet sizes, this is used to approximate how full or +empty the link is. + +This is rather circumspect and doesn't always arrive at proper +results. For example, what is the actual link speed of an interface +that is not really able to transmit the full 100mbit/s of data, +perhaps because of a badly implemented driver? A PCMCIA network card +will also never achieve 100mbit/s because of the way the bus is +designed - again, how do we calculate the idle time? + +The physical link bandwidth may be ill defined in case of not-quite-real +network devices like PPP over Ethernet or PPTP over TCP/IP. The effective +bandwidth in that case is probably determined by the efficiency of pipes +to userspace - which not defined. + +During operations, the effective idletime is measured using an +exponential weighted moving average (EWMA), which considers recent +packets to be exponentially more important than past ones. The Unix +loadaverage is calculated in the same way. + +The calculated idle time is subtracted from the EWMA measured one, +the resulting number is called 'avgidle'. A perfectly loaded link has +an avgidle of zero: packets arrive exactly at the calculated +interval. + +An overloaded link has a negative avgidle and if it gets too negative, +CBQ throttles and is then 'overlimit'. + +Conversely, an idle link might amass a huge avgidle, which would then +allow infinite bandwidths after a few hours of silence. To prevent +this, avgidle is capped at +.B maxidle. + +If overlimit, in theory, the CBQ could throttle itself for exactly the +amount of time that was calculated to pass between packets, and then +pass one packet, and throttle again. Due to timer resolution constraints, +this may not be feasible, see the +.B minburst +parameter below. + +.SH CLASSIFICATION +Within the one CBQ instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, CBQ starts at the root and uses various methods to +determine which class should receive the data. If a verdict is reached, this +process is repeated for the recipient class which might have further +means of classifying traffic to its children, if any. + +CBQ has the following methods available to classify a packet to any child +classes. +.TP +(i) +.B skb->priority class encoding. +Can be set from userspace by an application with the +.B SO_PRIORITY +setsockopt. +The +.B skb->priority class encoding +only applies if the skb->priority holds a major:minor handle of an existing +class within this qdisc. +.TP +(ii) +tc filters attached to the class. +.TP +(iii) +The defmap of a class, as set with the +.B split & defmap +parameters. The defmap may contain instructions for each possible Linux packet +priority. + +.P +Each class also has a +.B level. +Leaf nodes, attached to the bottom of the class hierarchy, have a level of 0. +.SH CLASSIFICATION ALGORITHM + +Classification is a loop, which terminates when a leaf class is found. At any +point the loop may jump to the fallback algorithm. + +The loop consists of the following steps: +.TP +(i) +If the packet is generated locally and has a valid classid encoded within its +.B skb->priority, +choose it and terminate. + +.TP +(ii) +Consult the tc filters, if any, attached to this child. If these return +a class which is not a leaf class, restart loop from the class returned. +If it is a leaf, choose it and terminate. +.TP +(iii) +If the tc filters did not return a class, but did return a classid, +try to find a class with that id within this qdisc. +Check if the found class is of a lower +.B level +than the current class. If so, and the returned class is not a leaf node, +restart the loop at the found class. If it is a leaf node, terminate. +If we found an upward reference to a higher level, enter the fallback +algorithm. +.TP +(iv) +If the tc filters did not return a class, nor a valid reference to one, +consider the minor number of the reference to be the priority. Retrieve +a class from the defmap of this class for the priority. If this did not +contain a class, consult the defmap of this class for the +.B BEST_EFFORT +class. If this is an upward reference, or no +.B BEST_EFFORT +class was defined, +enter the fallback algorithm. If a valid class was found, and it is not a +leaf node, restart the loop at this class. If it is a leaf, choose it and +terminate. If +neither the priority distilled from the classid, nor the +.B BEST_EFFORT +priority yielded a class, enter the fallback algorithm. +.P +The fallback algorithm resides outside of the loop and is as follows. +.TP +(i) +Consult the defmap of the class at which the jump to fallback occured. If +the defmap contains a class for the +.B +priority +of the class (which is related to the TOS field), choose this class and +terminate. +.TP +(ii) +Consult the map for a class for the +.B BEST_EFFORT +priority. If found, choose it, and terminate. +.TP +(iii) +Choose the class at which break out to the fallback algorithm occured. Terminate. +.P +The packet is enqueued to the class which was chosen when either algorithm +terminated. It is therefore possible for a packet to be enqueued *not* at a +leaf node, but in the middle of the hierarchy. + +.SH LINK SHARING ALGORITHM +When dequeuing for sending to the network device, CBQ decides which of its +classes will be allowed to send. It does so with a Weighted Round Robin process +in which each class with packets gets a chance to send in turn. The WRR process +starts by asking the highest priority classes (lowest numerically - +highest semantically) for packets, and will continue to do so until they +have no more data to offer, in which case the process repeats for lower +priorities. + +.B CERTAINTY ENDS HERE, ANK PLEASE HELP + +Each class is not allowed to send at length though - they can only dequeue a +configurable amount of data during each round. + +If a class is about to go overlimit, and it is not +.B bounded +it will try to borrow avgidle from siblings that are not +.B isolated. +This process is repeated from the bottom upwards. If a class is unable +to borrow enough avgidle to send a packet, it is throttled and not asked +for a packet for enough time for the avgidle to increase above zero. + +.B I REALLY NEED HELP FIGURING THIS OUT. REST OF DOCUMENT IS PRETTY CERTAIN +.B AGAIN. + +.SH QDISC +The root qdisc of a CBQ class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the CBQ instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the CBQ can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional. +.TP +avpkt bytes +For calculations, the average packet size must be known. It is silently capped +at a minimum of 2/3 of the interface MTU. Mandatory. +.TP +bandwidth rate +To determine the idle time, CBQ must know the bandwidth of your underlying +physical interface, or parent qdisc. This is a vital parameter, more about it +later. Mandatory. +.TP +cell +The cell size determines he granularity of packet transmission time calculations. Has a sensible default. +.TP +mpu +A zero sized packet may still take time to transmit. This value is the lower +cap for packet transmission time calculations - packets smaller than this value +are still deemed to have this size. Defaults to zero. +.TP +ewma log +When CBQ needs to measure the average idle time, it does so using an +Exponentially Weighted Moving Average which smoothes out measurements into +a moving average. The EWMA LOG determines how much smoothing occurs. Defaults +to 5. Lower values imply greater sensitivity. Must be between 0 and 31. +.P +A CBQ qdisc does not shape out of its own accord. It only needs to know certain +parameters about the underlying link. Actual shaping is done in classes. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +weight weight +When dequeuing to the interface, classes are tried for traffic in a +round-robin fashion. Classes with a higher configured qdisc will generally +have more traffic to offer during each round, so it makes sense to allow +it to dequeue more traffic. All weights under a class are normalized, so +only the ratios matter. Defaults to the configured rate, unless the priority +of this class is maximal, in which case it is set to 1. +.TP +allot bytes +Allot specifies how many bytes a qdisc can dequeue +during each round of the process. This parameter is weighted using the +renormalized class weight described above. + +.TP +priority priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +rate rate +Maximum rate this class and all its children combined can send at. Mandatory. + +.TP +bandwidth rate +This is different from the bandwidth specified when creating a CBQ disc. Only +used to determine maxidle and offtime, which are only calculated when +specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. + +.TP +maxburst +This number of packets is used to calculate maxidle so that when +avgidle is at maxidle, this number of average packets can be burst +before avgidle drops to 0. Set it higher to be more tolerant of +bursts. You can't set maxidle directly, only via this parameter. + +.TP +minburst +As mentioned before, CBQ needs to throttle in case of +overlimit. The ideal solution is to do so for exactly the calculated +idle time, and pass 1 packet. However, Unix kernels generally have a +hard time scheduling events shorter than 10ms, so it is better to +throttle for a longer period, and then pass minburst packets in one +go, and then sleep minburst times longer. + +The time to wait is called the offtime. Higher values of minburst lead +to more accurate shaping in the long term, but to bigger bursts at +millisecond timescales. + +.TP +minidle +If avgidle is below 0, we are overlimits and need to wait until +avgidle will be big enough to send one packet. To prevent a sudden +burst from shutting down the link for a prolonged period of time, +avgidle is reset to minidle if it gets too low. + +Minidle is specified in negative microseconds, so 10 means that +avgidle is capped at -10us. + +.TP +bounded +Signifies that this class will not borrow bandwidth from its siblings. +.TP +isolated +Means that this class will not borrow bandwidth to its siblings + +.TP +split major:minor & defmap bitmap[/bitmap] +If consulting filters attached to a class did not give a verdict, +CBQ can also classify based on the packet's priority. There are 16 +priorities available, numbered from 0 to 15. + +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The +.B split +parameter tells CBQ at which class the decision must be made, which should +be a (grand)parent of the class you are adding. + +As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' +configures class 10:0 to send packets with priorities 6 and 7 to 10:1. + +The complimentary configuration would then +be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' +Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. +.TP +estimator interval timeconstant +CBQ can measure how much bandwidth each class is using, which tc filters +can use to classify packets with. In order to determine the bandwidth +it uses a very simple estimator that measures once every +.B interval +microseconds how much traffic has passed. This again is a EWMA, for which +the time constant can be specified, also in microseconds. The +.B time constant +corresponds to the sluggishness of the measurement or, conversely, to the +sensitivity of the average to short bursts. Higher values mean less +sensitivity. + + + +.SH SOURCES +.TP +o +Sally Floyd and Van Jacobson, "Link-sharing and Resource +Management Models for Packet Networks", +IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 + +.TP +o +Sally Floyd, "Notes on CBQ and Guarantee Service", 1995 + +.TP +o +Sally Floyd, "Notes on Class-Based Queueing: Setting +Parameters", 1996 + +.TP +o +Sally Floyd and Michael Speer, "Experimental Results +for Class-Based Queueing", 1998, not published. + + + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-cbq.8 b/man/man8/tc-cbq.8 new file mode 100644 index 0000000..79fb93b --- /dev/null +++ b/man/man8/tc-cbq.8 @@ -0,0 +1,353 @@ +.TH CBQ 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +CBQ \- Class Based Queueing +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] cbq [ allot +bytes +.B ] avpkt +bytes +.B bandwidth +rate +.B [ cell +bytes +.B ] [ ewma +log +.B ] [ mpu +bytes +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] cbq allot +bytes +.B [ bandwidth +rate +.B ] [ rate +rate +.B ] prio +priority +.B [ weight +weight +.B ] [ minburst +packets +.B ] [ maxburst +packets +.B ] [ ewma +log +.B ] [ cell +bytes +.B ] avpkt +bytes +.B [ mpu +bytes +.B ] [ bounded isolated ] [ split +handle +.B & defmap +defmap +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +Class Based Queueing is a classful qdisc that implements a rich +linksharing hierarchy of classes. It contains shaping elements as +well as prioritizing capabilities. Shaping is performed using link +idle time calculations based on the timing of dequeue events and +underlying link bandwidth. + +.SH SHAPING ALGORITHM +When shaping a 10mbit/s connection to 1mbit/s, the link will +be idle 90% of the time. If it isn't, it needs to be throttled so that it +IS idle 90% of the time. + +During operations, the effective idletime is measured using an +exponential weighted moving average (EWMA), which considers recent +packets to be exponentially more important than past ones. The Unix +loadaverage is calculated in the same way. + +The calculated idle time is subtracted from the EWMA measured one, +the resulting number is called 'avgidle'. A perfectly loaded link has +an avgidle of zero: packets arrive exactly at the calculated +interval. + +An overloaded link has a negative avgidle and if it gets too negative, +CBQ throttles and is then 'overlimit'. + +Conversely, an idle link might amass a huge avgidle, which would then +allow infinite bandwidths after a few hours of silence. To prevent +this, avgidle is capped at +.B maxidle. + +If overlimit, in theory, the CBQ could throttle itself for exactly the +amount of time that was calculated to pass between packets, and then +pass one packet, and throttle again. Due to timer resolution constraints, +this may not be feasible, see the +.B minburst +parameter below. + +.SH CLASSIFICATION +Within the one CBQ instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, CBQ starts at the root and uses various methods to +determine which class should receive the data. + +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. + +The following actions are performed, in order at each node we visit, until one +sends us to another node, or terminates the process. +.TP +(i) +Consult filters attached to the class. If sent to a leafnode, we are done. +Otherwise, restart. +.TP +(ii) +Consult the defmap for the priority assigned to this packet, which depends +on the TOS bits. Check if the referral is leafless, otherwise restart. +.TP +(iii) +Ask the defmap for instructions for the 'best effort' priority. Check the +answer for leafness, otherwise restart. +.TP +(iv) +If none of the above returned with an instruction, enqueue at this node. +.P +This algorithm makes sure that a packet always ends up somewhere, even while +you are busy building your configuration. + +For more details, see +.BR tc-cbq-details(8). + +.SH LINK SHARING ALGORITHM +When dequeuing for sending to the network device, CBQ decides which of its +classes will be allowed to send. It does so with a Weighted Round Robin process +in which each class with packets gets a chance to send in turn. The WRR process +starts by asking the highest priority classes (lowest numerically - +highest semantically) for packets, and will continue to do so until they +have no more data to offer, in which case the process repeats for lower +priorities. + +Classes by default borrow bandwidth from their siblings. A class can be +prevented from doing so by declaring it 'bounded'. A class can also indicate +its unwillingness to lend out bandwidth by being 'isolated'. + +.SH QDISC +The root of a CBQ qdisc class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the CBQ instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the CBQ can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional, but very useful if classes +will be generated within this qdisc. +.TP +allot bytes +This allotment is the 'chunkiness' of link sharing and is used for determining packet +transmission time tables. The qdisc allot differs slightly from the class allot discussed +below. Optional. Defaults to a reasonable value, related to avpkt. +.TP +avpkt bytes +The average size of a packet is needed for calculating maxidle, and is also used +for making sure 'allot' has a safe value. Mandatory. +.TP +bandwidth rate +To determine the idle time, CBQ must know the bandwidth of your underlying +physical interface, or parent qdisc. This is a vital parameter, more about it +later. Mandatory. +.TP +cell +The cell size determines he granularity of packet transmission time calculations. Has a sensible default. +.TP +mpu +A zero sized packet may still take time to transmit. This value is the lower +cap for packet transmission time calculations - packets smaller than this value +are still deemed to have this size. Defaults to zero. +.TP +ewma log +When CBQ needs to measure the average idle time, it does so using an +Exponentially Weighted Moving Average which smoothes out measurements into +a moving average. The EWMA LOG determines how much smoothing occurs. Lower +values imply greater sensitivity. Must be between 0 and 31. Defaults +to 5. +.P +A CBQ qdisc does not shape out of its own accord. It only needs to know certain +parameters about the underlying link. Actual shaping is done in classes. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +weight weight +When dequeuing to the interface, classes are tried for traffic in a +round-robin fashion. Classes with a higher configured qdisc will generally +have more traffic to offer during each round, so it makes sense to allow +it to dequeue more traffic. All weights under a class are normalized, so +only the ratios matter. Defaults to the configured rate, unless the priority +of this class is maximal, in which case it is set to 1. +.TP +allot bytes +Allot specifies how many bytes a qdisc can dequeue +during each round of the process. This parameter is weighted using the +renormalized class weight described above. Silently capped at a minimum of +3/2 avpkt. Mandatory. + +.TP +prio priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +avpkt +See the QDISC section. + +.TP +rate rate +Maximum rate this class and all its children combined can send at. Mandatory. + +.TP +bandwidth rate +This is different from the bandwidth specified when creating a CBQ disc! Only +used to determine maxidle and offtime, which are only calculated when +specifying maxburst or minburst. Mandatory if specifying maxburst or minburst. + +.TP +maxburst +This number of packets is used to calculate maxidle so that when +avgidle is at maxidle, this number of average packets can be burst +before avgidle drops to 0. Set it higher to be more tolerant of +bursts. You can't set maxidle directly, only via this parameter. + +.TP +minburst +As mentioned before, CBQ needs to throttle in case of +overlimit. The ideal solution is to do so for exactly the calculated +idle time, and pass 1 packet. However, Unix kernels generally have a +hard time scheduling events shorter than 10ms, so it is better to +throttle for a longer period, and then pass minburst packets in one +go, and then sleep minburst times longer. + +The time to wait is called the offtime. Higher values of minburst lead +to more accurate shaping in the long term, but to bigger bursts at +millisecond timescales. Optional. + +.TP +minidle +If avgidle is below 0, we are overlimits and need to wait until +avgidle will be big enough to send one packet. To prevent a sudden +burst from shutting down the link for a prolonged period of time, +avgidle is reset to minidle if it gets too low. + +Minidle is specified in negative microseconds, so 10 means that +avgidle is capped at -10us. Optional. + +.TP +bounded +Signifies that this class will not borrow bandwidth from its siblings. +.TP +isolated +Means that this class will not borrow bandwidth to its siblings + +.TP +split major:minor & defmap bitmap[/bitmap] +If consulting filters attached to a class did not give a verdict, +CBQ can also classify based on the packet's priority. There are 16 +priorities available, numbered from 0 to 15. + +The defmap specifies which priorities this class wants to receive, +specified as a bitmap. The Least Significant Bit corresponds to priority +zero. The +.B split +parameter tells CBQ at which class the decision must be made, which should +be a (grand)parent of the class you are adding. + +As an example, 'tc class add ... classid 10:1 cbq .. split 10:0 defmap c0' +configures class 10:0 to send packets with priorities 6 and 7 to 10:1. + +The complimentary configuration would then +be: 'tc class add ... classid 10:2 cbq ... split 10:0 defmap 3f' +Which would send all packets 0, 1, 2, 3, 4 and 5 to 10:1. +.TP +estimator interval timeconstant +CBQ can measure how much bandwidth each class is using, which tc filters +can use to classify packets with. In order to determine the bandwidth +it uses a very simple estimator that measures once every +.B interval +microseconds how much traffic has passed. This again is a EWMA, for which +the time constant can be specified, also in microseconds. The +.B time constant +corresponds to the sluggishness of the measurement or, conversely, to the +sensitivity of the average to short bursts. Higher values mean less +sensitivity. + +.SH BUGS +The actual bandwidth of the underlying link may not be known, for example +in the case of PPoE or PPTP connections which in fact may send over a +pipe, instead of over a physical device. CBQ is quite resilient to major +errors in the configured bandwidth, probably a the cost of coarser shaping. + +Default kernels rely on coarse timing information for making decisions. These +may make shaping precise in the long term, but inaccurate on second long scales. + +See +.BR tc-cbq-details(8) +for hints on how to improve this. + +.SH SOURCES +.TP +o +Sally Floyd and Van Jacobson, "Link-sharing and Resource +Management Models for Packet Networks", +IEEE/ACM Transactions on Networking, Vol.3, No.4, 1995 + +.TP +o +Sally Floyd, "Notes on CBQ and Guaranteed Service", 1995 + +.TP +o +Sally Floyd, "Notes on Class-Based Queueing: Setting +Parameters", 1996 + +.TP +o +Sally Floyd and Michael Speer, "Experimental Results +for Class-Based Queueing", 1998, not published. + + + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-htb.8 b/man/man8/tc-htb.8 new file mode 100644 index 0000000..f61b818 --- /dev/null +++ b/man/man8/tc-htb.8 @@ -0,0 +1,150 @@ +.TH HTB 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +HTB \- Hierarchy Token Bucket +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] htb [ default +minor-id +.B ] + +.B tc class ... dev +dev +.B parent +major:[minor] +.B [ classid +major:minor +.B ] htb rate +rate +.B [ ceil +rate +.B ] burst +bytes +.B [ cburst +bytes +.B ] [ prio +priority +.B ] + +.SH DESCRIPTION +HTB is meant as a more understandable and intuitive replacement for +the CBQ qdisc in Linux. Both CBQ and HTB help you to control the use +of the outbound bandwidth on a given link. Both allow you to use one +physical link to simulate several slower links and to send different +kinds of traffic on different simulated links. In both cases, you have +to specify how to divide the physical link into simulated links and +how to decide which simulated link to use for a given packet to be sent. + +Unlike CBQ, HTB shapes traffic based on the Token Bucket Filter algorithm +which does not depend on interface characteristics and so does not need to +know the underlying bandwidth of the outgoing interface. + +.SH SHAPING ALGORITHM +Shaping works as documented in +.B tc-tbf (8). + +.SH CLASSIFICATION +Within the one HRB instance many classes may exist. Each of these classes +contains another qdisc, by default +.BR tc-pfifo (8). + +When enqueueing a packet, HTB starts at the root and uses various methods to +determine which class should receive the data. + +In the absence of uncommon configuration options, the process is rather easy. +At each node we look for an instruction, and then go to the class the +instruction refers us to. If the class found is a barren leaf-node (without +children), we enqueue the packet there. If it is not yet a leaf node, we do +the whole thing over again starting from that node. + +The following actions are performed, in order at each node we visit, until one +sends us to another node, or terminates the process. +.TP +(i) +Consult filters attached to the class. If sent to a leafnode, we are done. +Otherwise, restart. +.TP +(ii) +If none of the above returned with an instruction, enqueue at this node. +.P +This algorithm makes sure that a packet always ends up somewhere, even while +you are busy building your configuration. + +.SH LINK SHARING ALGORITHM +FIXME + +.SH QDISC +The root of a HTB qdisc class tree has the following parameters: + +.TP +parent major:minor | root +This mandatory parameter determines the place of the HTB instance, either at the +.B root +of an interface or within an existing class. +.TP +handle major: +Like all other qdiscs, the HTB can be assigned a handle. Should consist only +of a major number, followed by a colon. Optional, but very useful if classes +will be generated within this qdisc. +.TP +default minor-id +Unclassified traffic gets sent to the class with this minor-id. + +.SH CLASSES +Classes have a host of parameters to configure their operation. + +.TP +parent major:minor +Place of this class within the hierarchy. If attached directly to a qdisc +and not to another class, minor can be omitted. Mandatory. +.TP +classid major:minor +Like qdiscs, classes can be named. The major number must be equal to the +major number of the qdisc to which it belongs. Optional, but needed if this +class is going to have children. +.TP +prio priority +In the round-robin process, classes with the lowest priority field are tried +for packets first. Mandatory. + +.TP +rate rate +Maximum rate this class and all its children are guaranteed. Mandatory. + +.TP +ceil rate +Maximum rate at which a class can send, if its parent has bandwidth to spare. +Defaults to the configured rate, which implies no borrowing + +.TP +burst bytes +Amount of bytes that can be burst at +.B ceil +speed, in excess of the configured +.B rate. +Should be at least as high as the highest burst of all children. + +.TP +cburst bytes +Amount of bytes that can be burst at 'infinite' speed, in other words, as fast +as the interface can transmit them. For perfect evening out, should be equal to at most one average +packet. Should be at least as high as the highest cburst of all children. + +.SH NOTES +Due to Unix timing constraints, the maximum ceil rate is not infinite and may in fact be quite low. On Intel, +there are 100 timer events per second, the maximum rate is that rate at which 'burst' bytes are sent each timer tick. +From this, the mininum burst size for a specified rate can be calculated. For i386, a 10mbit rate requires a 12 kilobyte +burst as 100*12kb*8 equals 10mbit. + +.SH SEE ALSO +.BR tc (8) +.P +HTB website: http://luxik.cdi.cz/~devik/qos/htb/ +.SH AUTHOR +Martin Devera <devik@cdi.cz>. This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-pbfifo.8 b/man/man8/tc-pbfifo.8 new file mode 100644 index 0000000..8dda4bb --- /dev/null +++ b/man/man8/tc-pbfifo.8 @@ -0,0 +1,72 @@ +.TH PBFIFO 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +pfifo \- Packet limited First In, First Out queue +.P +bfifo \- Byte limited First In, First Out queue + +.SH SYNOPSIS +.B tc qdisc ... add pfifo +.B [ limit +packets +.B ] +.P +.B tc qdisc ... add bfifo +.B [ limit +bytes +.B ] + +.SH DESCRIPTION +The pfifo and bfifo qdiscs are unadorned First In, First Out queues. They are the +simplest queues possible and therefore have no overhead. +.B pfifo +constrains the queue size as measured in packets. +.B bfifo +does so as measured in bytes. + +Like all non-default qdiscs, they maintain statistics. This might be a reason to prefer +pfifo or bfifo over the default. + +.SH ALGORITHM +A list of packets is maintained, when a packet is enqueued it gets inserted at the tail of +a list. When a packet needs to be sent out to the network, it is taken from the head of the list. + +If the list is too long, no further packets are allowed on. This is called 'tail drop'. + +.SH PARAMETERS +.TP +limit +Maximum queue size. Specified in bytes for bfifo, in packets for pfifo. For pfifo, defaults +to the interface txqueuelen, as specified with +.BR ifconfig (8) +or +.BR ip (8). + +For bfifo, it defaults to the txqueuelen multiplied by the interface MTU. + +.SH OUTPUT +The output of +.B tc -s qdisc ls +contains the limit, either in packets or in bytes, and the number of bytes +and packets actually sent. An unsent and dropped packet only appears between braces +and is not counted as 'Sent'. + +In this example, the queue length is 100 packets, 45894 bytes were sent over 681 packets. +No packets were dropped, and as the pfifo queue does not slow down packets, there were also no +overlimits: +.P +.nf +# tc -s qdisc ls dev eth0 +qdisc pfifo 8001: dev eth0 limit 100p + Sent 45894 bytes 681 pkts (dropped 0, overlimits 0) +.fi + +If a backlog occurs, this is displayed as well. +.SH SEE ALSO +.BR tc (8) + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru> + +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-pfifo_fast.8 b/man/man8/tc-pfifo_fast.8 new file mode 100644 index 0000000..43ab166 --- /dev/null +++ b/man/man8/tc-pfifo_fast.8 @@ -0,0 +1,59 @@ +.TH PFIFO_FAST 8 "10 January 2002" "iproute2" "Linux" +.SH NAME +pfifo_fast \- three-band first in, first out queue + +.SH DESCRIPTION +pfifo_fast is the default qdisc of each interface. + +Whenever an interface is created, the pfifo_fast qdisc is automatically used +as a queue. If another qdisc is attached, it preempts the default +pfifo_fast, which automatically returns to function when an existing qdisc +is detached. + +In this sense this qdisc is magic, and unlike other qdiscs. + +.SH ALGORITHM +The algorithm is very similar to that of the classful +.BR tc-prio (8) +qdisc. +.B pfifo_fast +is like three +.BR tc-pfifo (8) +queues side by side, where packets can be enqueued in any of the three bands +based on their Type of Service bits or assigned priority. + +Not all three bands are dequeued simultaneously - as long as lower bands +have traffic, higher bands are never dequeued. This can be used to +prioritize interactive traffic or penalize 'lowest cost' traffic. + +Each band can be txqueuelen packets long, as configured with +.BR ifconfig (8) +or +.BR ip (8). +Additional packets coming in are not enqueued but are instead dropped. + +See +.BR tc-prio (8) +for complete details on how TOS bits are translated into bands. +.SH PARAMETERS +.TP +txqueuelen +The length of the three bands depends on the interface txqueuelen, as +specified with +.BR ifconfig (8) +or +.BR ip (8). + +.SH BUGS +Does not maintain statistics and does not show up in tc qdisc ls. This is because +it is the automatic default in the absence of a configured qdisc. + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru> + +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-prio.8 b/man/man8/tc-prio.8 new file mode 100644 index 0000000..e942e62 --- /dev/null +++ b/man/man8/tc-prio.8 @@ -0,0 +1,187 @@ +.TH PRIO 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +PRIO \- Priority qdisc +.SH SYNOPSIS +.B tc qdisc ... dev +dev +.B ( parent +classid +.B | root) [ handle +major: +.B ] prio [ bands +bands +.B ] [ priomap +band,band,band... +.B ] [ estimator +interval timeconstant +.B ] + +.SH DESCRIPTION +The PRIO qdisc is a simple classful queueing discipline that contains +an arbitrary number of classes of differing priority. The classes are +dequeued in numerical descending order of priority. PRIO is a scheduler +and never delays packets - it is a work-conserving qdisc, though the qdiscs +contained in the classes may not be. + +Very useful for lowering latency when there is no need for slowing down +traffic. + +.SH ALGORITHM +On creation with 'tc qdisc add', a fixed number of bands is created. Each +band is a class, although is not possible to add classes with 'tc qdisc +add', the number of bands to be created must instead be specified on the +commandline attaching PRIO to its root. + +When dequeueing, band 0 is tried first and only if it did not deliver a +packet does PRIO try band 1, and so onwards. Maximum reliability packets +should therefore go to band 0, minimum delay to band 1 and the rest to band +2. + +As the PRIO qdisc itself will have minor number 0, band 0 is actually +major:1, band 1 is major:2, etc. For major, substitute the major number +assigned to the qdisc on 'tc qdisc add' with the +.B handle +parameter. + +.SH CLASSIFICATION +Three methods are available to PRIO to determine in which band a packet will +be enqueued. +.TP +From userspace +A process with sufficient privileges can encode the destination class +directly with SO_PRIORITY, see +.BR tc(7). +.TP +with a tc filter +A tc filter attached to the root qdisc can point traffic directly to a class +.TP +with the priomap +Based on the packet priority, which in turn is derived from the Type of +Service assigned to the packet. +.P +Only the priomap is specific to this qdisc. +.SH QDISC PARAMETERS +.TP +bands +Number of bands. If changed from the default of 3, +.B priomap +must be updated as well. +.TP +priomap +The priomap maps the priority of +a packet to a class. The priority can either be set directly from userspace, +or be derived from the Type of Service of the packet. + +Determines how packet priorities, as assigned by the kernel, map to +bands. Mapping occurs based on the TOS octet of the packet, which looks like +this: + +.nf +0 1 2 3 4 5 6 7 ++---+---+---+---+---+---+---+---+ +| | | | +|PRECEDENCE | TOS |MBZ| +| | | | ++---+---+---+---+---+---+---+---+ +.fi + +The four TOS bits (the 'TOS field') are defined as: + +.nf +Binary Decimcal Meaning +----------------------------------------- +1000 8 Minimize delay (md) +0100 4 Maximize throughput (mt) +0010 2 Maximize reliability (mr) +0001 1 Minimize monetary cost (mmc) +0000 0 Normal Service +.fi + +As there is 1 bit to the right of these four bits, the actual value of the +TOS field is double the value of the TOS bits. Tcpdump -v -v shows you the +value of the entire TOS field, not just the four bits. It is the value you +see in the first column of this table: + +.nf +TOS Bits Means Linux Priority Band +------------------------------------------------------------ +0x0 0 Normal Service 0 Best Effort 1 +0x2 1 Minimize Monetary Cost 1 Filler 2 +0x4 2 Maximize Reliability 0 Best Effort 1 +0x6 3 mmc+mr 0 Best Effort 1 +0x8 4 Maximize Throughput 2 Bulk 2 +0xa 5 mmc+mt 2 Bulk 2 +0xc 6 mr+mt 2 Bulk 2 +0xe 7 mmc+mr+mt 2 Bulk 2 +0x10 8 Minimize Delay 6 Interactive 0 +0x12 9 mmc+md 6 Interactive 0 +0x14 10 mr+md 6 Interactive 0 +0x16 11 mmc+mr+md 6 Interactive 0 +0x18 12 mt+md 4 Int. Bulk 1 +0x1a 13 mmc+mt+md 4 Int. Bulk 1 +0x1c 14 mr+mt+md 4 Int. Bulk 1 +0x1e 15 mmc+mr+mt+md 4 Int. Bulk 1 +.fi + +The second column contains the value of the relevant +four TOS bits, followed by their translated meaning. For example, 15 stands +for a packet wanting Minimal Montetary Cost, Maximum Reliability, Maximum +Throughput AND Minimum Delay. + +The fourth column lists the way the Linux kernel interprets the TOS bits, by +showing to which Priority they are mapped. + +The last column shows the result of the default priomap. On the commandline, +the default priomap looks like this: + + 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 + +This means that priority 4, for example, gets mapped to band number 1. +The priomap also allows you to list higher priorities (> 7) which do not +correspond to TOS mappings, but which are set by other means. + +This table from RFC 1349 (read it for more details) explains how +applications might very well set their TOS bits: + +.nf +TELNET 1000 (minimize delay) +FTP + Control 1000 (minimize delay) + Data 0100 (maximize throughput) + +TFTP 1000 (minimize delay) + +SMTP + Command phase 1000 (minimize delay) + DATA phase 0100 (maximize throughput) + +Domain Name Service + UDP Query 1000 (minimize delay) + TCP Query 0000 + Zone Transfer 0100 (maximize throughput) + +NNTP 0001 (minimize monetary cost) + +ICMP + Errors 0000 + Requests 0000 (mostly) + Responses <same as request> (mostly) +.fi + + +.SH CLASSES +PRIO classes cannot be configured further - they are automatically created +when the PRIO qdisc is attached. Each class however can contain yet a +further qdisc. + +.SH BUGS +Large amounts of traffic in the lower bands can cause starvation of higher +bands. Can be prevented by attaching a shaper (for example, +.BR tc-tbf(8) +to these bands to make sure they cannot dominate the link. + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>, J Hadi Salim +<hadi@cyberus.ca>. This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-red.8 b/man/man8/tc-red.8 new file mode 100644 index 0000000..d02b411 --- /dev/null +++ b/man/man8/tc-red.8 @@ -0,0 +1,131 @@ +.TH RED 8 "13 December 2001" "iproute2" "Linux" +.SH NAME +red \- Random Early Detection +.SH SYNOPSIS +.B tc qdisc ... red +.B limit +bytes +.B min +bytes +.B max +bytes +.B avpkt +bytes +.B burst +packets +.B [ ecn ] [ bandwidth +rate +.B ] probability +chance + +.SH DESCRIPTION +Random Early Detection is a classless qdisc which manages its queue size +smartly. Regular queues simply drop packets from the tail when they are +full, which may not be the optimal behaviour. RED also performs tail drop, +but does so in a more gradual way. + +Once the queue hits a certain average length, packets enqueued have a +configurable chance of being marked (which may mean dropped). This chance +increases linearly up to a point called the +.B max +average queue length, although the queue might get bigger. + +This has a host of benefits over simple taildrop, while not being processor +intensive. It prevents synchronous retransmits after a burst in traffic, +which cause further retransmits, etc. + +The goal is the have a small queue size, which is good for interactivity +while not disturbing TCP/IP traffic with too many sudden drops after a burst +of traffic. + +Depending on if ECN is configured, marking either means dropping or +purely marking a packet as overlimit. +.SH ALGORITHM +The average queue size is used for determining the marking +probability. This is calculated using an Exponential Weighted Moving +Average, which can be more or less sensitive to bursts. + +When the average queue size is below +.B min +bytes, no packet will ever be marked. When it exceeds +.B min, +the probability of doing so climbs linearly up +to +.B probability, +until the average queue size hits +.B max +bytes. Because +.B probability +is normally not set to 100%, the queue size might +conceivably rise above +.B max +bytes, so the +.B limit +parameter is provided to set a hard maximum for the size of the queue. + +.SH PARAMETERS +.TP +min +Average queue size at which marking becomes a possibility. +.TP +max +At this average queue size, the marking probability is maximal. Should be at +least twice +.B min +to prevent synchronous retransmits, higher for low +.B min. +.TP +probability +Maximum probability for marking, specified as a floating point +number from 0.0 to 1.0. Suggested values are 0.01 or 0.02 (1 or 2%, +respectively). +.TP +limit +Hard limit on the real (not average) queue size in bytes. Further packets +are dropped. Should be set higher than max+burst. It is advised to set this +a few times higher than +.B max. +.TP +burst +Used for determining how fast the average queue size is influenced by the +real queue size. Larger values make the calculation more sluggish, allowing +longer bursts of traffic before marking starts. Real life experiments +support the following guideline: (min+min+max)/(3*avpkt). +.TP +avpkt +Specified in bytes. Used with burst to determine the time constant for +average queue size calculations. 1000 is a good value. +.TP +bandwidth +This rate is used for calculating the average queue size after some +idle time. Should be set to the bandwidth of your interface. Does not mean +that RED will shape for you! Optional. +.TP +ecn +As mentioned before, RED can either 'mark' or 'drop'. Explicit Congestion +Notification allows RED to notify remote hosts that their rate exceeds the +amount of bandwidth available. Non-ECN capable hosts can only be notified by +dropping a packet. If this parameter is specified, packets which indicate +that their hosts honor ECN will only be marked and not dropped, unless the +queue size hits +.B limit +bytes. Needs a tc binary with RED support compiled in. Recommended. + +.SH SEE ALSO +.BR tc (8) + +.SH SOURCES +.TP +o +Floyd, S., and Jacobson, V., Random Early Detection gateways for +Congestion Avoidance. http://www.aciri.org/floyd/papers/red/red.html +.TP +o +Some changes to the algorithm by Alexey N. Kuznetsov. + +.SH AUTHORS +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>, Alexey Makarenko +<makar@phoenix.kharkov.ua>, J Hadi Salim <hadi@nortelnetworks.com>. +This manpage maintained by bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-sfq.8 b/man/man8/tc-sfq.8 new file mode 100644 index 0000000..337c795 --- /dev/null +++ b/man/man8/tc-sfq.8 @@ -0,0 +1,107 @@ +.TH TC 8 "8 December 2001" "iproute2" "Linux" +.SH NAME +sfq \- Stochastic Fairness Queueing +.SH SYNOPSIS +.B tc qdisc ... perturb +seconds +.B quantum +bytes + +.SH DESCRIPTION + +Stochastic Fairness Queueing is a classless queueing discipline available for +traffic control with the +.BR tc (8) +command. + +SFQ does not shape traffic but only schedules the transmission of packets, based on 'flows'. +The goal is to ensure fairness so that each flow is able to send data in turn, thus preventing +any single flow from drowning out the rest. + +This may in fact have some effect in mitigating a Denial of Service attempt. + +SFQ is work-conserving and therefore always delivers a packet if it has one available. +.SH ALGORITHM +On enqueueing, each packet is assigned to a hash bucket, based on +.TP +(i) +Source address +.TP +(ii) +Destination address +.TP +(iii) +Source port +.P +If these are available. SFQ knows about ipv4 and ipv6 and also UDP, TCP and ESP. +Packets with other protocols are hashed based on the 32bits representation of their +destination and the socket they belong to. A flow corresponds mostly to a TCP/IP +connection. + +Each of these buckets should represent a unique flow. Because multiple flows may +get hashed to the same bucket, the hashing algorithm is perturbed at configurable +intervals so that the unfairness lasts only for a short while. Perturbation may +however cause some inadvertent packet reordering to occur. + +When dequeuing, each hashbucket with data is queried in a round robin fashion. + +The compile time maximum length of the SFQ is 128 packets, which can be spread over +at most 128 buckets of 1024 available. In case of overflow, tail-drop is performed +on the fullest bucket, thus maintaining fairness. + +.SH PARAMETERS +.TP +perturb +Interval in seconds for queue algorithm perturbation. Defaults to 0, which means that +no perturbation occurs. Do not set too low for each perturbation may cause some packet +reordering. Advised value: 10 +.TP +quantum +Amount of bytes a flow is allowed to dequeue during a round of the round robin process. +Defaults to the MTU of the interface which is also the advised value and the minimum value. + +.SH EXAMPLE & USAGE + +To attach to device ppp0: +.P +# tc qdisc add dev ppp0 root sfq perturb 10 +.P +Please note that SFQ, like all non-shaping (work-conserving) qdiscs, is only useful +if it owns the queue. +This is the case when the link speed equals the actually available bandwidth. This holds +for regular phone modems, ISDN connections and direct non-switched ethernet links. +.P +Most often, cable modems and DSL devices do not fall into this category. The same holds +for when connected to a switch and trying to send data to a congested segment also +connected to the switch. +.P +In this case, the effective queue does not reside within Linux and is therefore not +available for scheduling. +.P +Embed SFQ in a classful qdisc to make sure it owns the queue. + +.SH SOURCE +.TP +o +Paul E. McKenney "Stochastic Fairness Queuing", +IEEE INFOCOMM'90 Proceedings, San Francisco, 1990. + +.TP +o +Paul E. McKenney "Stochastic Fairness Queuing", +"Interworking: Research and Experience", v.2, 1991, p.113-131. + +.TP +o +See also: +M. Shreedhar and George Varghese "Efficient Fair +Queuing using Deficit Round Robin", Proc. SIGCOMM 95. + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc-tbf.8 b/man/man8/tc-tbf.8 new file mode 100644 index 0000000..3abb238 --- /dev/null +++ b/man/man8/tc-tbf.8 @@ -0,0 +1,138 @@ +.TH TC 8 "13 December 2001" "iproute2" "Linux" +.SH NAME +tbf \- Token Bucket Filter +.SH SYNOPSIS +.B tc qdisc ... tbf rate +rate +.B burst +bytes/cell +.B ( latency +ms +.B | limit +bytes +.B ) [ mpu +bytes +.B [ peakrate +rate +.B mtu +bytes/cell +.B ] ] +.P +burst is also known as buffer and maxburst. mtu is also known as minburst. +.SH DESCRIPTION + +The Token Bucket Filter is a classless queueing discipline available for +traffic control with the +.BR tc (8) +command. + +TBF is a pure shaper and never schedules traffic. It is non-work-conserving and may throttle +itself, although packets are available, to ensure that the configured rate is not exceeded. +On all platforms except for Alpha, +it is able to shape up to 1mbit/s of normal traffic with ideal minimal burstiness, +sending out data exactly at the configured rates. + +Much higher rates are possible but at the cost of losing the minimal burstiness. In that +case, data is on average dequeued at the configured rate but may be sent much faster at millisecond +timescales. Because of further queues living in network adaptors, this is often not a problem. + +Kernels with a higher 'HZ' can achieve higher rates with perfect burstiness. On Alpha, HZ is ten +times higher, leading to a 10mbit/s limit to perfection. These calculations hold for packets of on +average 1000 bytes. + +.SH ALGORITHM +As the name implies, traffic is filtered based on the expenditure of +.B tokens. +Tokens roughly correspond to bytes, with the additional constraint that each packet consumes +some tokens, no matter how small it is. This reflects the fact that even a zero-sized packet occupies +the link for some time. + +On creation, the TBF is stocked with tokens which correspond to the amount of traffic that can be burst +in one go. Tokens arrive at a steady rate, until the bucket is full. + +If no tokens are available, packets are queued, up to a configured limit. The TBF now +calculates the token deficit, and throttles until the first packet in the queue can be sent. + +If it is not acceptable to burst out packets at maximum speed, a peakrate can be configured +to limit the speed at which the bucket empties. This peakrate is implemented as a second TBF +with a very small bucket, so that it doesn't burst. + +To achieve perfection, the second bucket may contain only a single packet, which leads to +the earlier mentioned 1mbit/s limit. + +This limit is caused by the fact that the kernel can only throttle for at minimum 1 'jiffy', which depends +on HZ as 1/HZ. For perfect shaping, only a single packet can get sent per jiffy - for HZ=100, this means 100 +packets of on average 1000 bytes each, which roughly corresponds to 1mbit/s. + +.SH PARAMETERS +See +.BR tc (8) +for how to specify the units of these values. +.TP +limit or latency +Limit is the number of bytes that can be queued waiting for tokens to become +available. You can also specify this the other way around by setting the +latency parameter, which specifies the maximum amount of time a packet can +sit in the TBF. The latter calculation takes into account the size of the +bucket, the rate and possibly the peakrate (if set). These two parameters +are mutually exclusive. +.TP +burst +Also known as buffer or maxburst. +Size of the bucket, in bytes. This is the maximum amount of bytes that tokens can be available for instantaneously. +In general, larger shaping rates require a larger buffer. For 10mbit/s on Intel, you need at least 10kbyte buffer +if you want to reach your configured rate! + +If your buffer is too small, packets may be dropped because more tokens arrive per timer tick than fit in your bucket. +The minimum buffer size can be calculated by dividing the rate by HZ. + +Token usage calculations are performed using a table which by default has a resolution of 8 packets. +This resolution can be changed by specifying the +.B cell +size with the burst. For example, to specify a 6000 byte buffer with a 16 +byte cell size, set a burst of 6000/16. You will probably never have to set +this. Must be an integral power of 2. +.TP +mpu +A zero-sized packet does not use zero bandwidth. For ethernet, no packet uses less than 64 bytes. The Minimum Packet Unit +determines the minimal token usage (specified in bytes) for a packet. Defaults to zero. +.TP +rate +The speed knob. See remarks above about limits! See +.BR tc (8) +for units. +.PP +Furthermore, if a peakrate is desired, the following parameters are available: + +.TP +peakrate +Maximum depletion rate of the bucket. Limited to 1mbit/s on Intel, 10mbit/s on Alpha. The peakrate does +not need to be set, it is only necessary if perfect millisecond timescale shaping is required. + +.TP +mtu/minburst +Specifies the size of the peakrate bucket. For perfect accuracy, should be set to the MTU of the interface. +If a peakrate is needed, but some burstiness is acceptable, this size can be raised. A 3000 byte minburst +allows around 3mbit/s of peakrate, given 1000 byte packets. + +Like the regular burstsize you can also specify a +.B cell +size. +.SH EXAMPLE & USAGE + +To attach a TBF with a sustained maximum rate of 0.5mbit/s, a peakrate of 1.0mbit/s, +a 5kilobyte buffer, with a pre-bucket queue size limit calculated so the TBF causes +at most 70ms of latency, with perfect peakrate behaviour, issue: +.P +# tc qdisc add dev eth0 root tbf rate 0.5mbit \\ + burst 5kb latency 70ms peakrate 1mbit \\ + minburst 1540 + +.SH SEE ALSO +.BR tc (8) + +.SH AUTHOR +Alexey N. Kuznetsov, <kuznet@ms2.inr.ac.ru>. This manpage maintained by +bert hubert <ahu@ds9a.nl> + + diff --git a/man/man8/tc.8 b/man/man8/tc.8 new file mode 100644 index 0000000..b9b8039 --- /dev/null +++ b/man/man8/tc.8 @@ -0,0 +1,348 @@ +.TH TC 8 "16 December 2001" "iproute2" "Linux" +.SH NAME +tc \- show / manipulate traffic control settings +.SH SYNOPSIS +.B tc qdisc [ add | change | replace | link ] dev +DEV +.B +[ parent +qdisc-id +.B | root ] +.B [ handle +qdisc-id ] qdisc +[ qdisc specific parameters ] +.P + +.B tc class [ add | change | replace ] dev +DEV +.B parent +qdisc-id +.B [ classid +class-id ] qdisc +[ qdisc specific parameters ] +.P + +.B tc filter [ add | change | replace ] dev +DEV +.B [ parent +qdisc-id +.B | root ] protocol +protocol +.B prio +priority filtertype +[ filtertype specific parameters ] +.B flowid +flow-id + +.B tc [-s | -d ] qdisc show [ dev +DEV +.B ] +.P +.B tc [-s | -d ] class show dev +DEV +.P +.B tc filter show dev +DEV + +.SH DESCRIPTION +.B Tc +is used to configure Traffic Control in the Linux kernel. Traffic Control consists +of the following: + +.TP +SHAPING +When traffic is shaped, its rate of transmission is under control. Shaping may +be more than lowering the available bandwidth - it is also used to smooth out +bursts in traffic for better network behaviour. Shaping occurs on egress. + +.TP +SCHEDULING +By scheduling the transmission of packets it is possible to improve interactivity +for traffic that needs it while still guaranteeing bandwidth to bulk transfers. Reordering +is also called prioritizing, and happens only on egress. + +.TP +POLICING +Where shaping deals with transmission of traffic, policing pertains to traffic +arriving. Policing thus occurs on ingress. + +.TP +DROPPING +Traffic exceeding a set bandwidth may also be dropped forthwith, both on +ingress and on egress. + +.P +Processing of traffic is controlled by three kinds of objects: qdiscs, +classes and filters. + +.SH QDISCS +.B qdisc +is short for 'queueing discipline' and it is elementary to +understanding traffic control. Whenever the kernel needs to send a +packet to an interface, it is +.B enqueued +to the qdisc configured for that interface. Immediately afterwards, the kernel +tries to get as many packets as possible from the qdisc, for giving them +to the network adaptor driver. + +A simple QDISC is the 'pfifo' one, which does no processing at all and is a pure +First In, First Out queue. It does however store traffic when the network interface +can't handle it momentarily. + +.SH CLASSES +Some qdiscs can contain classes, which contain further qdiscs - traffic may +then be enqueued in any of the inner qdiscs, which are within the +.B classes. +When the kernel tries to dequeue a packet from such a +.B classful qdisc +it can come from any of the classes. A qdisc may for example prioritize +certain kinds of traffic by trying to dequeue from certain classes +before others. + +.SH FILTERS +A +.B filter +is used by a classful qdisc to determine in which class a packet will +be enqueued. Whenever traffic arrives at a class with subclasses, it needs +to be classified. Various methods may be employed to do so, one of these +are the filters. All filters attached to the class are called, until one of +them returns with a verdict. If no verdict was made, other criteria may be +available. This differs per qdisc. + +It is important to notice that filters reside +.B within +qdiscs - they are not masters of what happens. + +.SH CLASSLESS QDISCS +The classless qdiscs are: +.TP +[p|b]fifo +Simplest usable qdisc, pure First In, First Out behaviour. Limited in +packets or in bytes. +.TP +pfifo_fast +Standard qdisc for 'Advanced Router' enabled kernels. Consists of a three-band +queue which honors Type of Service flags, as well as the priority that may be +assigned to a packet. +.TP +red +Random Early Detection simulates physical congestion by randomly dropping +packets when nearing configured bandwidth allocation. Well suited to very +large bandwidth applications. +.TP +sfq +Stochastic Fairness Queueing reorders queued traffic so each 'session' +gets to send a packet in turn. +.TP +tbf +The Token Bucket Filter is suited for slowing traffic down to a precisely +configured rate. Scales well to large bandwidths. +.SH CONFIGURING CLASSLESS QDISCS +In the absence of classful qdiscs, classless qdiscs can only be attached at +the root of a device. Full syntax: +.P +.B tc qdisc add dev +DEV +.B root +QDISC QDISC-PARAMETERS + +To remove, issue +.P +.B tc qdisc del dev +DEV +.B root + +The +.B pfifo_fast +qdisc is the automatic default in the absence of a configured qdisc. + +.SH CLASSFUL QDISCS +The classful qdiscs are: +.TP +CBQ +Class Based Queueing implements a rich linksharing hierarchy of classes. +It contains shaping elements as well as prioritizing capabilities. Shaping is +performed using link idle time calculations based on average packet size and +underlying link bandwidth. The latter may be ill-defined for some interfaces. +.TP +HTB +The Hierarchy Token Bucket implements a rich linksharing hierarchy of +classes with an emphasis on conforming to existing practices. HTB facilitates +guaranteeing bandwidth to classes, while also allowing specification of upper +limits to inter-class sharing. It contains shaping elements, based on TBF and +can prioritize classes. +.TP +PRIO +The PRIO qdisc is a non-shaping container for a configurable number of +classes which are dequeued in order. This allows for easy prioritization +of traffic, where lower classes are only able to send if higher ones have +no packets available. To facilitate configuration, Type Of Service bits are +honored by default. +.SH THEORY OF OPERATION +Classes form a tree, where each class has a single parent. +A class may have multiple children. Some qdiscs allow for runtime addition +of classes (CBQ, HTB) while others (PRIO) are created with a static number of +children. + +Qdiscs which allow dynamic addition of classes can have zero or more +subclasses to which traffic may be enqueued. + +Furthermore, each class contains a +.B leaf qdisc +which by default has +.B pfifo +behaviour though another qdisc can be attached in place. This qdisc may again +contain classes, but each class can have only one leaf qdisc. + +When a packet enters a classful qdisc it can be +.B classified +to one of the classes within. Three criteria are available, although not all +qdiscs will use all three: +.TP +tc filters +If tc filters are attached to a class, they are consulted first +for relevant instructions. Filters can match on all fields of a packet header, +as well as on the firewall mark applied by ipchains or iptables. See +.BR tc-filters (8). +.TP +Type of Service +Some qdiscs have built in rules for classifying packets based on the TOS field. +.TP +skb->priority +Userspace programs can encode a class-id in the 'skb->priority' field using +the SO_PRIORITY option. +.P +Each node within the tree can have its own filters but higher level filters +may also point directly to lower classes. + +If classification did not succeed, packets are enqueued to the leaf qdisc +attached to that class. Check qdisc specific manpages for details, however. + +.SH NAMING +All qdiscs, classes and filters have IDs, which can either be specified +or be automatically assigned. + +IDs consist of a major number and a minor number, separated by a colon. + +.TP +QDISCS +A qdisc, which potentially can have children, +gets assigned a major number, called a 'handle', leaving the minor +number namespace available for classes. The handle is expressed as '10:'. +It is customary to explicitly assign a handle to qdiscs expected to have +children. + +.TP +CLASSES +Classes residing under a qdisc share their qdisc major number, but each have +a separate minor number called a 'classid' that has no relation to their +parent classes, only to their parent qdisc. The same naming custom as for +qdiscs applies. + +.TP +FILTERS +Filters have a three part ID, which is only needed when using a hashed +filter hierarchy, for which see +.BR tc-filters (8). +.SH UNITS +All parameters accept a floating point number, possibly followed by a unit. +.P +Bandwidths or rates can be specified in: +.TP +kbps +Kilobytes per second +.TP +mbps +Megabytes per second +.TP +kbit +Kilobits per second +.TP +mbit +Megabits per second +.TP +bps or a bare number +Bytes per second +.P +Amounts of data can be specified in: +.TP +kb or k +Kilobytes +.TP +mb or m +Megabytes +.TP +mbit +Megabits +.TP +kbit +Kilobits +.TP +b or a bare number +Bytes. +.P +Lengths of time can be specified in: +.TP +s, sec or secs +Whole seconds +.TP +ms, msec or msecs +Milliseconds +.TP +us, usec, usecs or a bare number +Microseconds. + +.SH TC COMMANDS +The following commands are available for qdiscs, classes and filter: +.TP +add +Add a qdisc, class or filter to a node. For all entities, a +.B parent +must be passed, either by passing its ID or by attaching directly to the root of a device. +When creating a qdisc or a filter, it can be named with the +.B handle +parameter. A class is named with the +.B classid +parameter. + +.TP +remove +A qdisc can be removed by specifying its handle, which may also be 'root'. All subclasses and their leaf qdiscs +are automatically deleted, as well as any filters attached to them. + +.TP +change +Some entities can be modified 'in place'. Shares the syntax of 'add', with the exception +that the handle cannot be changed and neither can the parent. In other words, +.B +change +cannot move a node. + +.TP +replace +Performs a nearly atomic remove/add on an existing node id. If the node does not exist yet +it is created. + +.TP +link +Only available for qdiscs and performs a replace where the node +must exist already. + + +.SH HISTORY +.B tc +was written by Alexey N. Kuznetsov and added in Linux 2.2. +.SH SEE ALSO +.BR tc-cbq (8), +.BR tc-htb (8), +.BR tc-sfq (8), +.BR tc-red (8), +.BR tc-tbf (8), +.BR tc-pfifo (8), +.BR tc-bfifo (8), +.BR tc-pfifo_fast (8), +.BR tc-filters (8) + +.SH AUTHOR +Manpage maintained by bert hubert (ahu@ds9a.nl) + diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 0000000..2ddf950 --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,35 @@ +SSOBJ=ss.o ssfilter.o +LNSTATOBJ=lnstat.o lnstat_util.o + +TARGETS=ss nstat ifstat rtacct arpd lnstat + +include ../Config + +all: $(TARGETS) + +ss: $(SSOBJ) $(LIBUTIL) + +nstat: nstat.c + $(CC) $(CFLAGS) $(LDFLAGS) -o nstat nstat.c -lm + +ifstat: ifstat.c + $(CC) $(CFLAGS) $(LDFLAGS) -o ifstat ifstat.c $(LIBNETLINK) -lm + +rtacct: rtacct.c + $(CC) $(CFLAGS) $(LDFLAGS) -o rtacct rtacct.c $(LIBNETLINK) -lm + +arpd: arpd.c + $(CC) $(CFLAGS) -I$(DBM_INCLUDE) $(LDFLAGS) -o arpd arpd.c $(LIBNETLINK) -ldb -lpthread + +ssfilter.c: ssfilter.y + bison ssfilter.y -o ssfilter.c + +lnstat: $(LNSTATOBJ) + +install: all + install -m 0755 -s $(TARGETS) $(DESTDIR)$(SBINDIR) + ln -sf $(SBINDIR)/lnstat $(DESTDIR)$(SBINDIR)/rtstat + ln -sf $(SBINDIR)/lnstat $(DESTDIR)$(SBINDIR)/ctstat + +clean: + rm -f *.o $(TARGETS) ssfilter.c diff --git a/misc/arpd b/misc/arpd new file mode 100755 index 0000000000000000000000000000000000000000..cb898a9fba2b760be23c973ad351ebf699d13012 GIT binary patch literal 41062 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%0D^JaS{?GPyN+mmsZxNcW<~% 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 literal 0 HcmV?d00001 diff --git a/misc/arpd.c b/misc/arpd.c new file mode 100644 index 0000000..85b2a1c --- /dev/null +++ b/misc/arpd.c @@ -0,0 +1,846 @@ +/* + * arpd.c ARP helper daemon. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <syslog.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <netdb.h> +#include <db_185.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/if_packet.h> +#include <linux/filter.h> + +#include "libnetlink.h" +#include "utils.h" + +int resolve_hosts; + +DB *dbase; +char *dbname = "/var/lib/arpd/arpd.db"; + +int ifnum; +int *ifvec; +char **ifnames; + +struct dbkey +{ + __u32 iface; + __u32 addr; +}; + +#define IS_NEG(x) (((__u8*)(x))[0] == 0xFF) +#define NEG_TIME(x) (((x)[2]<<24)|((x)[3]<<16)|((x)[4]<<8)|(x)[5]) +#define NEG_AGE(x) ((__u32)time(NULL) - NEG_TIME((__u8*)x)) +#define NEG_VALID(x) (NEG_AGE(x) < negative_timeout) +#define NEG_CNT(x) (((__u8*)(x))[1]) + +struct rtnl_handle rth; + +struct pollfd pset[2]; +int udp_sock = -1; + +volatile int do_exit; +volatile int do_sync; +volatile int do_stats; + +struct { + unsigned long arp_new; + unsigned long arp_change; + + unsigned long app_recv; + unsigned long app_success; + unsigned long app_bad; + unsigned long app_neg; + unsigned long app_suppressed; + + unsigned long kern_neg; + unsigned long kern_new; + unsigned long kern_change; + + unsigned long probes_sent; + unsigned long probes_suppressed; +} stats; + +int active_probing; +int negative_timeout = 60; +int no_kernel_broadcasts; +int broadcast_rate = 1000; +int broadcast_burst = 3000; + +void usage(void) +{ + fprintf(stderr, +"Usage: arpd [ -lk ] [ -a N ] [ -b dbase ] [ -f file ] [ interfaces ]\n"); + exit(1); +} + +int handle_if(int ifindex) +{ + int i; + + if (ifnum == 0) + return 1; + + for (i=0; i<ifnum; i++) + if (ifvec[i] == ifindex) + return 1; + return 0; +} + +int sysctl_adjusted; + +void do_sysctl_adjustments(void) +{ + int i; + + if (!ifnum) + return; + + for (i=0; i<ifnum; i++) { + char buf[128]; + FILE *fp; + + if (active_probing) { + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + if (no_kernel_broadcasts) + strcpy(buf, "0\n"); + else + sprintf(buf, "%d\n", active_probing>=2 ? 1 : 3-active_probing); + fputs(buf, fp); + fclose(fp); + } + } + + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + sprintf(buf, "%d\n", active_probing<=1 ? 1 : active_probing); + fputs(buf, fp); + fclose(fp); + } + } + sysctl_adjusted = 1; +} + +void undo_sysctl_adjustments(void) +{ + int i; + + if (!sysctl_adjusted) + return; + + for (i=0; i<ifnum; i++) { + char buf[128]; + FILE *fp; + + if (active_probing) { + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/mcast_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + strcpy(buf, "3\n"); + fputs(buf, fp); + fclose(fp); + } + } + sprintf(buf, "/proc/sys/net/ipv4/neigh/%s/app_solicit", ifnames[i]); + if ((fp = fopen(buf, "w")) != NULL) { + strcpy(buf, "0\n"); + fputs(buf, fp); + fclose(fp); + } + } + sysctl_adjusted = 0; +} + + +int send_probe(int ifindex, __u32 addr) +{ + struct ifreq ifr; + struct sockaddr_in dst; + int len; + unsigned char buf[256]; + struct arphdr *ah = (struct arphdr*)buf; + unsigned char *p = (unsigned char *)(ah+1); + struct sockaddr_ll sll; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = ifindex; + if (ioctl(udp_sock, SIOCGIFNAME, &ifr)) + return -1; + if (ioctl(udp_sock, SIOCGIFHWADDR, &ifr)) + return -1; + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) + return -1; + if (setsockopt(udp_sock, SOL_SOCKET, SO_BINDTODEVICE, ifr.ifr_name, strlen(ifr.ifr_name)+1) < 0) + return -1; + + dst.sin_family = AF_INET; + dst.sin_port = htons(1025); + dst.sin_addr.s_addr = addr; + if (connect(udp_sock, (struct sockaddr*)&dst, sizeof(dst)) < 0) + return -1; + len = sizeof(dst); + if (getsockname(udp_sock, (struct sockaddr*)&dst, &len) < 0) + return -1; + + ah->ar_hrd = htons(ifr.ifr_hwaddr.sa_family); + ah->ar_pro = htons(ETH_P_IP); + ah->ar_hln = 6; + ah->ar_pln = 4; + ah->ar_op = htons(ARPOP_REQUEST); + + memcpy(p, ifr.ifr_hwaddr.sa_data, ah->ar_hln); + p += ah->ar_hln; + + memcpy(p, &dst.sin_addr, 4); + p+=4; + + sll.sll_family = AF_PACKET; + memset(sll.sll_addr, 0xFF, sizeof(sll.sll_addr)); + sll.sll_ifindex = ifindex; + sll.sll_protocol = htons(ETH_P_ARP); + memcpy(p, &sll.sll_addr, ah->ar_hln); + p+=ah->ar_hln; + + memcpy(p, &addr, 4); + p+=4; + + len = sendto(pset[0].fd, buf, p-buf, 0, (struct sockaddr*)&sll, sizeof(sll)); + if (len < 0) + return -1; + stats.probes_sent++; + return 0; +} + +/* Be very tough on sending probes: 1 per second with burst of 3. */ + +int queue_active_probe(int ifindex, __u32 addr) +{ + static struct timeval prev; + static int buckets; + struct timeval now; + + gettimeofday(&now, NULL); + if (prev.tv_sec) { + int diff = (now.tv_sec-prev.tv_sec)*1000+(now.tv_usec-prev.tv_usec)/1000; + buckets += diff; + } else { + buckets = broadcast_burst; + } + if (buckets > broadcast_burst) + buckets = broadcast_burst; + if (buckets >= broadcast_rate && !send_probe(ifindex, addr)) { + buckets -= broadcast_rate; + prev = now; + return 0; + } + stats.probes_suppressed++; + return -1; +} + +int respond_to_kernel(int ifindex, __u32 addr, char *lla, int llalen) +{ + struct { + struct nlmsghdr n; + struct ndmsg ndm; + char buf[256]; + } req; + + memset(&req.n, 0, sizeof(req.n)); + memset(&req.ndm, 0, sizeof(req.ndm)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWNEIGH; + req.ndm.ndm_family = AF_INET; + req.ndm.ndm_state = NUD_STALE; + req.ndm.ndm_ifindex = ifindex; + req.ndm.ndm_type = RTN_UNICAST; + + addattr_l(&req.n, sizeof(req), NDA_DST, &addr, 4); + addattr_l(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); + return rtnl_send(&rth, (char*)&req, req.n.nlmsg_len) <= 0; +} + +void prepare_neg_entry(__u8 *ndata, __u32 stamp) +{ + ndata[0] = 0xFF; + ndata[1] = 0; + ndata[2] = stamp>>24; + ndata[3] = stamp>>16; + ndata[4] = stamp>>8; + ndata[5] = stamp; +} + + +int do_one_request(struct nlmsghdr *n) +{ + struct ndmsg *ndm = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[NDA_MAX+1]; + struct dbkey key; + DBT dbkey, dbdat; + int do_acct = 0; + + if (n->nlmsg_type == NLMSG_DONE) { + dbase->sync(dbase, 0); + + /* Now we have at least mirror of kernel db, so that + * may start real resolution. + */ + do_sysctl_adjustments(); + return 0; + } + + if (n->nlmsg_type != RTM_GETNEIGH && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ndm)); + if (len < 0) + return -1; + + if (ndm->ndm_family != AF_INET || + (ifnum && !handle_if(ndm->ndm_ifindex)) || + ndm->ndm_flags || + ndm->ndm_type != RTN_UNICAST || + !(ndm->ndm_state&~NUD_NOARP)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len); + + if (!tb[NDA_DST]) + return 0; + + key.iface = ndm->ndm_ifindex; + memcpy(&key.addr, RTA_DATA(tb[NDA_DST]), 4); + dbkey.data = &key; + dbkey.size = sizeof(key); + + if (dbase->get(dbase, &dbkey, &dbdat, 0) != 0) { + dbdat.data = 0; + dbdat.size = 0; + } + + if (n->nlmsg_type == RTM_GETNEIGH) { + if (!(n->nlmsg_flags&NLM_F_REQUEST)) + return 0; + + if (!(ndm->ndm_state&(NUD_PROBE|NUD_INCOMPLETE))) { + stats.app_bad++; + return 0; + } + + if (ndm->ndm_state&NUD_PROBE) { + /* If we get this, kernel still has some valid + * address, but unicast probing failed and host + * is either dead or changed its mac address. + * Kernel is going to initiate broadcast resolution. + * OK, we invalidate our information as well. + */ + if (dbdat.data && !IS_NEG(dbdat.data)) + stats.app_neg++; + + dbase->del(dbase, &dbkey, 0); + } else { + /* If we get this kernel does not have any information. + * If we have something tell this to kernel. */ + stats.app_recv++; + if (dbdat.data && !IS_NEG(dbdat.data)) { + stats.app_success++; + respond_to_kernel(key.iface, key.addr, dbdat.data, dbdat.size); + return 0; + } + + /* Sheeit! We have nothing to tell. */ + /* If we have recent negative entry, be silent. */ + if (dbdat.data && NEG_VALID(dbdat.data)) { + if (NEG_CNT(dbdat.data) >= active_probing) { + stats.app_suppressed++; + return 0; + } + do_acct = 1; + } + } + + if (active_probing && + queue_active_probe(ndm->ndm_ifindex, key.addr) == 0 && + do_acct) { + NEG_CNT(dbdat.data)++; + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } else if (n->nlmsg_type == RTM_NEWNEIGH) { + if (n->nlmsg_flags&NLM_F_REQUEST) + return 0; + + if (ndm->ndm_state&NUD_FAILED) { + /* Kernel was not able to resolve. Host is dead. + * Create negative entry if it is not present + * or renew it if it is too old. */ + if (!dbdat.data || + !IS_NEG(dbdat.data) || + !NEG_VALID(dbdat.data)) { + __u8 ndata[6]; + stats.kern_neg++; + prepare_neg_entry(ndata, time(NULL)); + dbdat.data = ndata; + dbdat.size = sizeof(ndata); + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } else if (tb[NDA_LLADDR]) { + if (dbdat.data && !IS_NEG(dbdat.data)) { + if (memcmp(RTA_DATA(tb[NDA_LLADDR]), dbdat.data, dbdat.size) == 0) + return 0; + stats.kern_change++; + } else { + stats.kern_new++; + } + dbdat.data = RTA_DATA(tb[NDA_LLADDR]); + dbdat.size = RTA_PAYLOAD(tb[NDA_LLADDR]); + dbase->put(dbase, &dbkey, &dbdat, 0); + } + } + return 0; +} + +void load_initial_table(void) +{ + rtnl_wilddump_request(&rth, AF_INET, RTM_GETNEIGH); +} + +void get_kern_msg(void) +{ + int status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[8192]; + struct msghdr msg = { + (void*)&nladdr, sizeof(nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + memset(&nladdr, 0, sizeof(nladdr)); + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + status = recvmsg(rth.fd, &msg, MSG_DONTWAIT); + + if (status <= 0) + return; + + if (msg.msg_namelen != sizeof(nladdr)) + return; + + if (nladdr.nl_pid) + return; + + for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { + int len = h->nlmsg_len; + int l = len - sizeof(*h); + + if (l < 0 || len > status) + return; + + if (do_one_request(h) < 0) + return; + + status -= NLMSG_ALIGN(len); + h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); + } +} + +/* Receive gratuitous ARP messages and store them, that's all. */ +void get_arp_pkt(void) +{ + unsigned char buf[1024]; + struct sockaddr_ll sll; + int sll_len = sizeof(sll); + struct arphdr *a = (struct arphdr*)buf; + struct dbkey key; + DBT dbkey, dbdat; + int n; + + n = recvfrom(pset[0].fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr*)&sll, &sll_len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + syslog(LOG_ERR, "recvfrom: %m"); + return; + } + + if (ifnum && !handle_if(sll.sll_ifindex)) + return; + + /* Sanity checks */ + + if (n < sizeof(*a) || + (a->ar_op != htons(ARPOP_REQUEST) && + a->ar_op != htons(ARPOP_REPLY)) || + a->ar_pln != 4 || + a->ar_pro != htons(ETH_P_IP) || + a->ar_hln != sll.sll_halen || + sizeof(*a) + 2*4 + 2*a->ar_hln > n) + return; + + key.iface = sll.sll_ifindex; + memcpy(&key.addr, (char*)(a+1) + a->ar_hln, 4); + + /* DAD message, ignore. */ + if (key.addr == 0) + return; + + dbkey.data = &key; + dbkey.size = sizeof(key); + + if (dbase->get(dbase, &dbkey, &dbdat, 0) == 0 && !IS_NEG(dbdat.data)) { + if (memcmp(dbdat.data, a+1, dbdat.size) == 0) + return; + stats.arp_change++; + } else { + stats.arp_new++; + } + + dbdat.data = a+1; + dbdat.size = a->ar_hln; + dbase->put(dbase, &dbkey, &dbdat, 0); +} + +void catch_signal(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; +#ifdef SA_INTERRUPT + sa.sa_flags = SA_INTERRUPT; +#endif + sigaction(sig, &sa, NULL); +} + +#include <setjmp.h> +sigjmp_buf env; +volatile int in_poll; + +void sig_exit(int signo) +{ + do_exit = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void sig_sync(int signo) +{ + do_sync = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void sig_stats(int signo) +{ + do_sync = 1; + do_stats = 1; + if (in_poll) + siglongjmp(env, 1); +} + +void send_stats(void) +{ + syslog(LOG_INFO, "arp_rcv: n%lu c%lu app_rcv: tot %lu hits %lu bad %lu neg %lu sup %lu", + stats.arp_new, stats.arp_change, + + stats.app_recv, stats.app_success, + stats.app_bad, stats.app_neg, stats.app_suppressed + ); + syslog(LOG_INFO, "kern: n%lu c%lu neg %lu arp_send: %lu rlim %lu", + stats.kern_new, stats.kern_change, stats.kern_neg, + + stats.probes_sent, stats.probes_suppressed + ); + do_stats = 0; +} + + +int main(int argc, char **argv) +{ + int opt; + int do_list = 0; + char *do_load = NULL; + + while ((opt = getopt(argc, argv, "h?b:lf:a:n:kR:B:")) != EOF) { + switch (opt) { + case 'b': + dbname = optarg; + break; + case 'f': + if (do_load) { + fprintf(stderr, "Duplicate option -f\n"); + usage(); + } + do_load = optarg; + break; + case 'l': + do_list = 1; + break; + case 'a': + active_probing = atoi(optarg); + break; + case 'n': + negative_timeout = atoi(optarg); + break; + case 'k': + no_kernel_broadcasts = 1; + break; + case 'R': + if ((broadcast_rate = atoi(optarg)) <= 0 || + (broadcast_rate = 1000/broadcast_rate) <= 0) { + fprintf(stderr, "Invalid ARP rate\n"); + exit(-1); + } + break; + case 'B': + if ((broadcast_burst = atoi(optarg)) <= 0 || + (broadcast_burst = 1000*broadcast_burst) <= 0) { + fprintf(stderr, "Invalid ARP burst\n"); + exit(-1); + } + break; + case 'h': + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 0) { + ifnum = argc; + ifnames = argv; + ifvec = malloc(argc*sizeof(int)); + if (!ifvec) { + perror("malloc"); + exit(-1); + } + } + + if ((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(-1); + } + + if (ifnum) { + int i; + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + for (i=0; i<ifnum; i++) { + strncpy(ifr.ifr_name, ifnames[i], IFNAMSIZ); + if (ioctl(udp_sock, SIOCGIFINDEX, &ifr)) { + perror("ioctl(SIOCGIFINDEX)"); + exit(-1);; + } + ifvec[i] = ifr.ifr_ifindex; + } + } + + dbase = dbopen(dbname, O_CREAT|O_RDWR, 0644, DB_HASH, NULL); + if (dbase == NULL) { + perror("db_open"); + exit(-1); + } + + if (do_load) { + char buf[128]; + FILE *fp; + struct dbkey k; + DBT dbkey, dbdat; + + dbkey.data = &k; + dbkey.size = sizeof(k); + + if (strcmp(do_load, "-") == 0 || strcmp(do_load, "--") == 0) { + fp = stdin; + } else if ((fp = fopen(do_load, "r")) == NULL) { + perror("fopen"); + goto do_abort; + } + + buf[sizeof(buf)-1] = 0; + while (fgets(buf, sizeof(buf)-1, fp)) { + __u8 b1[6]; + char ipbuf[128]; + char macbuf[128]; + + if (buf[0] == '#') + continue; + + if (sscanf(buf, "%u%s%s", &k.iface, ipbuf, macbuf) != 3) { + fprintf(stderr, "Wrong format of input file \"%s\"\n", do_load); + goto do_abort; + } + if (strncmp(macbuf, "FAILED:", 7) == 0) + continue; + if (!inet_aton(ipbuf, (struct in_addr*)&k.addr)) { + fprintf(stderr, "Invalid IP address: \"%s\"\n", ipbuf); + goto do_abort; + } + dbdat.data = hexstring_a2n(macbuf, b1, 6); + if (dbdat.data == NULL) + goto do_abort; + dbdat.size = 6; + + if (dbase->put(dbase, &dbkey, &dbdat, 0)) { + perror("hash->put"); + goto do_abort; + } + } + dbase->sync(dbase, 0); + if (fp != stdin) + fclose(fp); + } + + if (do_list) { + DBT dbkey, dbdat; + printf("%-8s %-15s %s\n", "#Ifindex", "IP", "MAC"); + while (dbase->seq(dbase, &dbkey, &dbdat, R_NEXT) == 0) { + struct dbkey *key = dbkey.data; + if (handle_if(key->iface)) { + if (!IS_NEG(dbdat.data)) { + __u8 b1[18]; + printf("%-8d %-15s %s\n", + key->iface, + inet_ntoa(*(struct in_addr*)&key->addr), + hexstring_n2a(dbdat.data, 6, b1, 18)); + } else { + printf("%-8d %-15s FAILED: %dsec ago\n", + key->iface, + inet_ntoa(*(struct in_addr*)&key->addr), + NEG_AGE(dbdat.data)); + } + } + } + } + + if (do_load || do_list) + goto out; + + pset[0].fd = socket(PF_PACKET, SOCK_DGRAM, 0); + if (pset[0].fd < 0) { + perror("socket"); + exit(-1); + } + + if (1) { + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ARP); + sll.sll_ifindex = (ifnum == 1 ? ifvec[0] : 0); + if (bind(pset[0].fd, (struct sockaddr*)&sll, sizeof(sll)) < 0) { + perror("bind"); + goto do_abort; + } + } + + if (rtnl_open(&rth, RTMGRP_NEIGH) < 0) { + perror("rtnl_open"); + goto do_abort; + } + pset[1].fd = rth.fd; + + load_initial_table(); + + if (1) { + int fd; + pid_t pid = fork(); + + if (pid > 0) + _exit(0); + if (pid < 0) { + perror("arpd: fork"); + goto do_abort; + } + + chdir("/"); + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + } + setsid(); + } + + openlog("arpd", LOG_PID | LOG_CONS, LOG_DAEMON); + catch_signal(SIGINT, sig_exit); + catch_signal(SIGTERM, sig_exit); + catch_signal(SIGHUP, sig_sync); + catch_signal(SIGUSR1, sig_stats); + +#define EVENTS (POLLIN|POLLPRI|POLLERR|POLLHUP) + pset[0].events = EVENTS; + pset[0].revents = 0; + pset[1].events = EVENTS; + pset[1].revents = 0; + + sigsetjmp(env, 1); + + for (;;) { + in_poll = 1; + + if (do_exit) + break; + if (do_sync) { + in_poll = 0; + dbase->sync(dbase, 0); + do_sync = 0; + in_poll = 1; + } + if (do_stats) + send_stats(); + if (poll(pset, 2, 30000) > 0) { + in_poll = 0; + if (pset[0].revents&EVENTS) + get_arp_pkt(); + if (pset[1].revents&EVENTS) + get_kern_msg(); + } else { + do_sync = 1; + } + } + + undo_sysctl_adjustments(); +out: + dbase->close(dbase); + exit(0); + +do_abort: + dbase->close(dbase); + exit(-1); +} diff --git a/misc/ifstat b/misc/ifstat new file mode 100755 index 0000000000000000000000000000000000000000..5a54b5134206e5bd89f65281a5d8e88744b53087 GIT binary patch literal 33086 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 literal 0 HcmV?d00001 diff --git a/misc/ifstat.c b/misc/ifstat.c new file mode 100644 index 0000000..1379a81 --- /dev/null +++ b/misc/ifstat.c @@ -0,0 +1,766 @@ +/* + * ifstat.c handy utility to read net interface statistics + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <signal.h> +#include <math.h> +#include <getopt.h> + +#include <libnetlink.h> +#include <linux/netdevice.h> + +#include <SNAPSHOT.h> + +int dump_zeros = 0; +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +int show_errors = 0; +double W; +char **patterns; +int npatterns; + +char info_source[128]; +int source_mismatch; + +#define MAXS (sizeof(struct net_device_stats)/sizeof(unsigned long)) + +struct ifstat_ent +{ + struct ifstat_ent *next; + char *name; + int ifindex; + unsigned long long val[MAXS]; + double rate[MAXS]; + unsigned long ival[MAXS]; +}; + +struct ifstat_ent *kern_db; +struct ifstat_ent *hist_db; + +static int match(const char *id) +{ + int i; + + if (npatterns == 0) + return 1; + + for (i=0; i<npatterns; i++) { + if (!fnmatch(patterns[i], id, 0)) + return 1; + } + return 0; +} + +static int get_nlmsg(const struct sockaddr_nl *who, + struct nlmsghdr *m, void *arg) +{ + struct ifinfomsg *ifi = NLMSG_DATA(m); + struct rtattr * tb[IFLA_MAX+1]; + int len = m->nlmsg_len; + struct ifstat_ent *n; + int i; + + if (m->nlmsg_type != RTM_NEWLINK) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) + return -1; + + if (!(ifi->ifi_flags&IFF_UP)) + return 0; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL) + return 0; + + n = malloc(sizeof(*n)); + if (!n) + abort(); + n->ifindex = ifi->ifi_index; + n->name = strdup(RTA_DATA(tb[IFLA_IFNAME])); + memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival)); + memset(&n->rate, 0, sizeof(n->rate)); + for (i=0; i<MAXS; i++) + n->val[i] = n->ival[i]; + n->next = kern_db; + kern_db = n; + return 0; +} + +void load_info(void) +{ + struct ifstat_ent *db, *n; + struct rtnl_handle rth; + + if (rtnl_open(&rth, 0) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, get_nlmsg, NULL, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + rtnl_close(&rth); + + db = kern_db; + kern_db = NULL; + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + +void load_raw_table(FILE *fp) +{ + char buf[4096]; + struct ifstat_ent *db = NULL; + struct ifstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *p; + char *next; + int i; + + if (buf[0] == '#') { + buf[strlen(buf)-1] = 0; + if (info_source[0] && strcmp(info_source, buf+1)) + source_mismatch = 1; + strncpy(info_source, buf+1, sizeof(info_source)-1); + continue; + } + if ((n = malloc(sizeof(*n))) == NULL) + abort(); + + if (!(p = strchr(buf, ' '))) + abort(); + *p++ = 0; + + if (sscanf(buf, "%d", &n->ifindex) != 1) + abort(); + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + + n->name = strdup(p); + p = next; + + for (i=0; i<MAXS; i++) { + unsigned rate; + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + if (sscanf(p, "%llu", n->val+i) != 1) + abort(); + n->ival[i] = (unsigned long)n->val[i]; + p = next; + if (!(next = strchr(p, ' '))) + abort(); + *next++ = 0; + if (sscanf(p, "%u", &rate) != 1) + abort(); + n->rate[i] = rate; + p = next; + } + n->next = db; + db = n; + } + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + +void dump_raw_db(FILE *fp, int to_hist) +{ + struct ifstat_ent *n, *h; + h = hist_db; + fprintf(fp, "#%s\n", info_source); + + for (n=kern_db; n; n=n->next) { + int i; + unsigned long long *vals = n->val; + double *rates = n->rate; + if (!match(n->name)) { + struct ifstat_ent *h1; + if (!to_hist) + continue; + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + vals = h1->val; + rates = h1->rate; + h = h1->next; + break; + } + } + } + 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"); + } +} + +/* use communication definitions of meg/kilo etc */ +static const unsigned long long giga = 1000000000ull; +static const unsigned long long mega = 1000000; +static const unsigned long long kilo = 1000; + +void format_rate(FILE *fp, unsigned long long *vals, double *rates, int i) +{ + char temp[64]; + if (vals[i] > giga) + fprintf(fp, "%7lluM ", vals[i]/mega); + else if (vals[i] > mega) + fprintf(fp, "%7lluK ", vals[i]/kilo); + else + fprintf(fp, "%8llu ", vals[i]); + + if (rates[i] > mega) { + sprintf(temp, "%uM", (unsigned)(rates[i]/mega)); + fprintf(fp, "%-6s ", temp); + } else if (rates[i] > kilo) { + sprintf(temp, "%uK", (unsigned)(rates[i]/kilo)); + fprintf(fp, "%-6s ", temp); + } else + fprintf(fp, "%-6u ", (unsigned)rates[i]); +} + +void format_pair(FILE *fp, unsigned long long *vals, int i, int k) +{ + char temp[64]; + if (vals[i] > giga) + fprintf(fp, "%7lluM ", vals[i]/mega); + else if (vals[i] > mega) + fprintf(fp, "%7lluK ", vals[i]/kilo); + else + fprintf(fp, "%8llu ", vals[i]); + + if (vals[k] > giga) { + sprintf(temp, "%uM", (unsigned)(vals[k]/mega)); + fprintf(fp, "%-6s ", temp); + } else if (vals[k] > mega) { + sprintf(temp, "%uK", (unsigned)(vals[k]/kilo)); + fprintf(fp, "%-6s ", temp); + } else + fprintf(fp, "%-6u ", (unsigned)vals[k]); +} + +void print_head(FILE *fp) +{ + fprintf(fp, "#%s\n", info_source); + fprintf(fp, "%-15s ", "Interface"); + + fprintf(fp, "%8s/%-6s ", "RX Pkts", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Pkts", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Data", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Data", "Rate"); + + if (!show_errors) { + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Errs", "Drop"); + fprintf(fp, "%8s/%-6s ", "TX Errs", "Drop"); + fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Coll", "Rate"); + } else { + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Errs", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Drop", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Over", "Rate"); + fprintf(fp, "%8s/%-6s\n","RX Leng", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "RX Crc", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Frm", "Rate"); + fprintf(fp, "%8s/%-6s ", "RX Fifo", "Rate"); + fprintf(fp, "%8s/%-6s\n","RX Miss", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "TX Errs", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Drop", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Coll", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Carr", "Rate"); + + fprintf(fp, "%-15s ", ""); + fprintf(fp, "%8s/%-6s ", "TX Abrt", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Fifo", "Rate"); + fprintf(fp, "%8s/%-6s ", "TX Hear", "Rate"); + fprintf(fp, "%8s/%-6s\n","TX Wind", "Rate"); + } +} + +void print_one_if(FILE *fp, struct ifstat_ent *n, unsigned long long *vals) +{ + int i; + fprintf(fp, "%-15s ", n->name); + for (i=0; i<4; i++) + format_rate(fp, vals, n->rate, i); + fprintf(fp, "\n"); + + if (!show_errors) { + fprintf(fp, "%-15s ", ""); + format_pair(fp, vals, 4, 6); + format_pair(fp, vals, 5, 7); + format_rate(fp, vals, n->rate, 11); + format_rate(fp, vals, n->rate, 9); + fprintf(fp, "\n"); + } else { + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 4); + format_rate(fp, vals, n->rate, 6); + format_rate(fp, vals, n->rate, 11); + format_rate(fp, vals, n->rate, 10); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 12); + format_rate(fp, vals, n->rate, 13); + format_rate(fp, vals, n->rate, 14); + format_rate(fp, vals, n->rate, 15); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 5); + format_rate(fp, vals, n->rate, 7); + format_rate(fp, vals, n->rate, 9); + format_rate(fp, vals, n->rate, 17); + fprintf(fp, "\n"); + + fprintf(fp, "%-15s ", ""); + format_rate(fp, vals, n->rate, 16); + format_rate(fp, vals, n->rate, 18); + format_rate(fp, vals, n->rate, 19); + format_rate(fp, vals, n->rate, 20); + fprintf(fp, "\n"); + } +} + + +void dump_kern_db(FILE *fp) +{ + struct ifstat_ent *n, *h; + h = hist_db; + + print_head(fp); + + for (n=kern_db; n; n=n->next) { + if (!match(n->name)) + continue; + print_one_if(fp, n, n->val); + } +} + + +void dump_incr_db(FILE *fp) +{ + struct ifstat_ent *n, *h; + h = hist_db; + + print_head(fp); + + for (n=kern_db; n; n=n->next) { + int i; + unsigned long long vals[MAXS]; + struct ifstat_ent *h1; + + memcpy(vals, n->val, sizeof(vals)); + + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + for (i = 0; i < MAXS; i++) + vals[i] -= h1->val[i]; + h = h1->next; + break; + } + } + if (!match(n->name)) + continue; + print_one_if(fp, n, vals); + } +} + + +static int children; + +void sigchild(int signo) +{ +} + +void update_db(int interval) +{ + struct ifstat_ent *n, *h; + + n = kern_db; + kern_db = NULL; + + load_info(); + + h = kern_db; + kern_db = n; + + for (n = kern_db; n; n = n->next) { + struct ifstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (h1->ifindex == n->ifindex) { + int i; + for (i = 0; i < MAXS; i++) { + if ((long)(h1->ival[i] - n->ival[i]) < 0) { + memset(n->ival, 0, sizeof(n->ival)); + break; + } + } + for (i = 0; i < MAXS; i++) { + double sample; + unsigned long incr = h1->ival[i] - n->ival[i]; + n->val[i] += incr; + n->ival[i] = h1->ival[i]; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + n->rate[i] += W*(sample-n->rate[i]); + } else if (interval >= 1000) { + if (interval >= time_constant) { + n->rate[i] = sample; + } else { + double w = W*(double)interval/scan_interval; + n->rate[i] += w*(sample-n->rate[i]); + } + } + } + + while (h != h1) { + struct ifstat_ent *tmp = h; + h = h->next; + free(tmp->name); + free(tmp); + }; + h = h1->next; + free(h1->name); + free(h1); + break; + } + } + } +} + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + load_info(); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + FILE *fp = fdopen(clnt, "w"); + if (fp) { + if (tdiff > 0) + update_db(tdiff); + dump_raw_db(fp, 0); + } + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n" +" -h, --help this message\n" +" -a, --ignore ignore history\n" +" -d, --scan=SECS sample every statistics every SECS\n" +" -e, --errors show errors\n" +" -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" +" -e, --errors show errors\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'}, + { "errors", 0, 0, 'e' }, + { "nooutput", 0, 0, 'n' }, + { "reset", 0, 0, 'r' }, + { "noupdate", 0, 0, 's' }, + { "interval", 1, 0, 't' }, + { "version", 0, 0, 'V' }, + { "zeros", 0, 0, 'z' }, + { "errors", 0, 0, 'e' }, + { "zeros", 0, 0, 'z' }, + { 0 } +}; + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + FILE *hist_fp = NULL; + int ch; + int fd; + + while ((ch = getopt_long(argc, argv, "hvVzrnasd:t:eK", + longopts, NULL)) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'e': + show_errors = 1; + break; + case 'd': + scan_interval = atoi(optarg) * 1000; + if (scan_interval <= 0) { + fprintf(stderr, "ifstat: invalid scan interval\n"); + exit(-1); + } + break; + case 't': + time_constant = atoi(optarg); + if (time_constant <= 0) { + fprintf(stderr, "ifstat: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("ifstat utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "ifstat%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("ifstat: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("ifstat: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("ifstat: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + patterns = argv; + npatterns = argc; + + if (getenv("IFSTAT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("IFSTAT_HISTORY")); + else + sprintf(hist_name, "%s/.ifstat.u%d", P_tmpdir, getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("ifstat: open history file"); + exit(-1); + } + if ((hist_fp = fdopen(fd, "r+")) == NULL) { + perror("ifstat: fdopen history file"); + exit(-1); + } + if (flock(fileno(hist_fp), LOCK_EX)) { + perror("ifstat: flock history file"); + exit(-1); + } + if (fstat(fileno(hist_fp), &stb) != 0) { + perror("ifstat: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "ifstat: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "ifstat: history is aged out, resetting\n"); + ftruncate(fileno(hist_fp), 0); + } + } + + load_raw_table(hist_fp); + + hist_db = kern_db; + kern_db = NULL; + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "ifstat0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + FILE *sfp = fdopen(fd, "r"); + load_raw_table(sfp); + if (hist_db && source_mismatch) { + fprintf(stderr, "ifstat: history is stale, ignoring it.\n"); + hist_db = NULL; + } + fclose(sfp); + } else { + if (fd >= 0) + close(fd); + if (hist_db && info_source[0] && strcmp(info_source, "kernel")) { + fprintf(stderr, "ifstat: history is stale, ignoring it.\n"); + hist_db = NULL; + info_source[0] = 0; + } + load_info(); + if (info_source[0] == 0) + strcpy(info_source, "kernel"); + } + + if (!no_output) { + if (ignore_history || hist_db == NULL) + dump_kern_db(stdout); + else + dump_incr_db(stdout); + } + if (!no_update) { + ftruncate(fileno(hist_fp), 0); + rewind(hist_fp); + dump_raw_db(hist_fp, 1); + fflush(hist_fp); + } + exit(0); +} diff --git a/misc/lnstat b/misc/lnstat new file mode 100755 index 0000000000000000000000000000000000000000..c59931a90587f955be55bf44f045a57b94e92822 GIT binary patch literal 19999 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 literal 0 HcmV?d00001 diff --git a/misc/lnstat.c b/misc/lnstat.c new file mode 100644 index 0000000..03e6f3f --- /dev/null +++ b/misc/lnstat.c @@ -0,0 +1,336 @@ +/* lnstat - Unified linux network statistics + * + * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * Based on original concept and ideas from predecessor rtstat.c: + * + * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se> + * Uppsala University, Sweden + * + * 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. + * + */ + +/* Maximum number of fields that can be displayed */ +#define MAX_FIELDS 64 + +/* Maximum number of header lines */ +#define HDR_LINES 10 + +/* default field width if none specified */ +#define FIELD_WIDTH_DEFAULT 8 +#define FIELD_WIDTH_MAX 20 + +#define DEFAULT_INTERVAL 2 + +#define HDR_LINE_LENGTH (MAX_FIELDS*FIELD_WIDTH_MAX) + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> + +#include "lnstat.h" + +static struct option opts[] = { + { "version", 0, NULL, 'V' }, + { "count", 1, NULL, 'c' }, + { "dump", 1, NULL, 'd' }, + { "file", 1, NULL, 'f' }, + { "help", 0, NULL, 'h' }, + { "interval", 1, NULL, 'i' }, + { "key", 1, NULL, 'k' }, + { "subject", 1, NULL, 's' }, + { "width", 1, NULL, 'w' }, +}; + +static int usage(char *name, int exit_code) +{ + fprintf(stderr, "%s Version %s\n", name, LNSTAT_VERSION); + fprintf(stderr, "Copyright (C) 2004 by Harald Welte " + "<laforge@gnumonks.org>\n"); + fprintf(stderr, "This program is free software licensed under GNU GPLv2" + "\nwith ABSOLUTELY NO WARRANTY.\n\n"); + fprintf(stderr, "Parameters:\n"); + fprintf(stderr, "\t-V --version\t\tPrint Version of Program\n"); + fprintf(stderr, "\t-c --count <count>\t" + "Print <count> number of intervals\n"); + fprintf(stderr, "\t-d --dumpt\t\t" + "Dump list of available files/keys\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" + "Set interval to 'intv' seconds\n"); + fprintf(stderr, "\t-k --keys k,k,k,...\tDisplay only keys specified\n"); + fprintf(stderr, "\t-s --subject [0-2]\t?\n"); + fprintf(stderr, "\t-w --width n,n,n,...\tWidth for each field\n"); + fprintf(stderr, "\n"); + + exit(exit_code); +} + +struct field_param { + const char *name; + struct lnstat_field *lf; + struct { + unsigned int width; + } print; +}; + +struct field_params { + unsigned int num; + struct field_param params[MAX_FIELDS]; +}; + +static void print_line(FILE *of, const struct lnstat_file *lnstat_files, + const struct field_params *fp) +{ + int i; + + for (i = 0; i < fp->num; i++) { + struct lnstat_field *lf = fp->params[i].lf; + char formatbuf[255]; + + snprintf(formatbuf, sizeof(formatbuf)-1, "%%%ulu|", + fp->params[i].print.width); + fprintf(of, formatbuf, lf->result); + } + fputc('\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) +{ + int i, j = 0; + struct lnstat_file *lf; + + /* no field specification on commandline, need to build default */ + if (!fps->num) { + for (lf = lnstat_files; lf; lf = lf->next) { + for (i = 0; i < lf->num_fields; i++) { + fps->params[j].lf = &lf->fields[i]; + fps->params[j].lf->file->interval.tv_sec = + interval; + if (!fps->params[j].print.width) + fps->params[j].print.width = + FIELD_WIDTH_DEFAULT; + j++; + } + } + fps->num = j; + return 1; + } + + for (i = 0; i < fps->num; i++) { + fps->params[i].lf = lnstat_find_field(lnstat_files, + fps->params[i].name); + if (!fps->params[i].lf) { + fprintf(stderr, "Field `%s' unknown\n", + fps->params[i].name); + return 0; + } + fps->params[i].lf->file->interval.tv_sec = interval; + if (!fps->params[i].print.width) + fps->params[i].print.width = FIELD_WIDTH_DEFAULT; + } + return 1; +} + +struct table_hdr { + int num_lines; + char *hdr[HDR_LINES]; +}; + +static struct table_hdr *build_hdr_string(struct lnstat_file *lnstat_files, + struct field_params *fps, + int linewidth) +{ + int h,i; + static struct table_hdr th; + int ofs = 0; + + for (i = 0; i < HDR_LINES; i++) { + th.hdr[i] = malloc(HDR_LINE_LENGTH); + memset(th.hdr[i], 0, sizeof(th.hdr[i])); + } + + for (i = 0; i < fps->num; i++) { + char *cname, *fname = fps->params[i].lf->name; + char fmt[12]; + unsigned int width = fps->params[i].print.width; + + snprintf(fmt, sizeof(fmt)-1, "%%%u.%us|", width, width); + + snprintf(th.hdr[0]+ofs, width+2, fmt, + fps->params[i].lf->file->basename); + + cname = fname; + for (h = 1; h < HDR_LINES; h++) { + if (cname - fname >= strlen(fname)) + snprintf(th.hdr[h]+ofs, width+2, fmt, ""); + else { + th.num_lines = h+1; + snprintf(th.hdr[h]+ofs, width+2, fmt, cname); + } + cname += width; + } + ofs += width+1; + } + /* fill in spaces */ + for (h = 1; h <= th.num_lines; h++) { + for (i = 0; i < ofs; i++) { + if (th.hdr[h][i] == '\0') + th.hdr[h][i] = ' '; + } + } + + return &th; +} + +static int print_hdr(FILE *of, struct table_hdr *th) +{ + int i; + + for (i = 0; i < th->num_lines; i++) { + fputs(th->hdr[i], of); + fputc('\n', of); + } + return 0; +} + + +int main(int argc, char **argv) +{ + struct lnstat_file *lnstat_files; + const char *basename; + int c; + int interval = DEFAULT_INTERVAL; + int hdr = 2; + enum { + MODE_DUMP, + MODE_NORMAL, + } mode = MODE_NORMAL; + + unsigned long count = 0; + static struct field_params fp; + int num_req_files = 0; + char *req_files[LNSTAT_MAX_FILES]; + + /* backwards compatibility mode for old tools */ + basename = strrchr(argv[0], '/'); + if (basename) + basename += 1; /* name after slash */ + else + basename = argv[0]; /* no slash */ + + if (!strcmp(basename, "rtstat")) { + /* rtstat compatibility mode */ + req_files[0] = "rt_cache"; + num_req_files = 1; + } else if (!strcmp(basename, "ctstat")) { + /* ctstat compatibility mode */ + req_files[0] = "ip_conntrack"; + num_req_files = 1; + } + + while ((c = getopt_long(argc, argv,"Vc:df:h?i:k:s:w:", + opts, NULL)) != -1) { + switch (c) { + int i, len = 0; + char *tmp, *tok; + case 'c': + count = strtoul(optarg, NULL, 0); + 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) + break; + for (tok = strtok(tmp, ","); + tok; + tok = strtok(NULL, ",")) { + if (fp.num >= MAX_FIELDS) + break; + fp.params[fp.num++].name = tok; + } + 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; + 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; + } + } + + lnstat_files = lnstat_scan_dir(PROC_NET_STAT, num_req_files, + (const char **) req_files); + + switch (mode) { + int i; + struct table_hdr *header; + case MODE_DUMP: + lnstat_dump(stderr, lnstat_files); + break; + case MODE_NORMAL: + + if (!map_field_params(lnstat_files, &fp, interval)) + exit(1); + + header = build_hdr_string(lnstat_files, &fp, 80); + if (!header) + exit(1); + + if (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); + sleep(interval); + } + } + + return 1; +} + diff --git a/misc/lnstat.h b/misc/lnstat.h new file mode 100644 index 0000000..06774ab --- /dev/null +++ b/misc/lnstat.h @@ -0,0 +1,43 @@ +#ifndef _LNSTAT_H +#define _LNSTAT_H + +#include <limits.h> + +#define LNSTAT_VERSION "0.02 041002" + +#define PROC_NET_STAT "/proc/net/stat" + +#define LNSTAT_MAX_FILES 32 +#define LNSTAT_MAX_FIELDS_PER_LINE 32 +#define LNSTAT_MAX_FIELD_NAME_LEN 32 + +struct lnstat_file; + +struct lnstat_field { + struct lnstat_file *file; + unsigned int num; /* field number in line */ + char name[LNSTAT_MAX_FIELD_NAME_LEN+1]; + unsigned long values[2]; /* two buffers for values */ + unsigned long result; +}; + +struct lnstat_file { + struct lnstat_file *next; + char path[PATH_MAX+1]; + char basename[NAME_MAX+1]; + struct timeval last_read; /* last time of read */ + struct timeval interval; /* interval */ + int compat; /* 1 == backwards compat mode */ + FILE *fp; + unsigned int num_fields; /* number of fields */ + struct lnstat_field fields[LNSTAT_MAX_FIELDS_PER_LINE]; +}; + + +struct lnstat_file *lnstat_scan_dir(const char *path, const int num_req_files, + const char **req_files); +int lnstat_update(struct lnstat_file *lnstat_files); +int lnstat_dump(FILE *outfd, struct lnstat_file *lnstat_files); +struct lnstat_field *lnstat_find_field(struct lnstat_file *lnstat_files, + const char *name); +#endif /* _LNSTAT_H */ diff --git a/misc/lnstat.o b/misc/lnstat.o new file mode 100644 index 0000000000000000000000000000000000000000..b272d8789126f144d4024ebdbeae86fa9f1f2166 GIT binary patch literal 11104 zcmeI1du&_P9mh|a0C891z{<$Bj9r)mDN9^CJeG#6jY(3j!G%K82jY>s_H}IH*pcsb z(~i=nIDuXnvuLX(_RxU+Q88&~XzbDPC@yUp$^bDYpb~>JX&Q~7sN+$}qxF8jbI(bC zz2po{+CP(aAU@}Oe!ufQzw`9H?v~!j(m7RCOr|Qfn4PRi%2;##$#NGjcCoWq9n<Yk z^x}6BtoS2-+V2O;yRdKwKlI|6m_613AG=WYDXgAiTB$b8Q=6LArpwi)g=*6Rwdp*y z=`yt`s5X5?ZTj|XChUbhH5Wek%-IKdmPky%|Dn~r!AfXV$UYHT8R`q$AMC<Gu3VvS zeP_)a#=^x;-&BvU6N(J-c9FN>TIc{Y$i;e$m&111J9hu+(5h12c?KBxYDMgQpXv7Q z_jlw&`qY?jx!4_{r?4>58!Yp6e+-2p_CI>;k9c&2V->5vaA-;4So=teU9R{h(P1{# zC#mY&5Akh+VIO+%c%|}h<ns^erR_YN9{a?}eO0{K*P`1!zQFiv-m#xTjD=0UmMZVq zPAKYI_u`~rk==Al=;qKZp<6@!d&CCjaqHtS2NzW;72TfV`#5-5av<KZd0+}p@xkB% zOa%7_UkV--Lq5mevvWRZdzT&1W0S#(UaINhB9+OIee+~3QOfTt9Ix_@KY0RrnkqvZ z*7dt3plCPfg>pmX`He@XA4FTJ&(|*EbHXWek?AosItNdxddIQ=xfg``b{mhccn)e8 zK3U?u`+Bh2FFJMzOt(AvFiKZe1uOPx=q8n*d4AvY`J(^M>Dyt@!J}MnI(o9>aO_~H zGUYU~$Nq{tWBMIfG}u=yC^vl&Dk4ZSPq;eT^d4@re1TI&G5Z1)d33$O{lz|CCoItT zz16VUim=Gb#>-YV5_>6B+}U*&W5L6Vj+?8)u|LD~>!tZOzyg#Ze;K0*^K!0UFKyGo zRWFWW)>FK-R=2ONkJu9$RC~u?%hydUt*wOyLNip^U71{TJon3GMF!pFxA)lmQiD-8 zqHb^S@ROM1^du(2vDcqs5N@b4$tNaMIPQS~zh0|Pp%dHu2Ke_}(TZhnp$(=CTIi)G zd|goFhe$6q-n8pWU=BS2h=r&2p)b(aF)wyEM2ff9hwQSw8|JYgSPmDa<~eH+>+?10 z#cmkOaX1AHda*WG*6mmIYJNW#_RP`kK3^@K8tlw|L*KgF1J^olT#Wb{?3;b_b$g!g z?Vw)V<f|=OP!-PM>c!%qhbnu+g?(ZxSGV{=h0i=&mg)A}5xWvu@Tn8YqCMslGa8{% zkFN>@q?T?^JKGJUFnpKT=0{<b8)0!9U=B-t^;HmbqnxojUx4lBYzJq03dgE8b?_-o z`x@_P4#%F;i#5;VOod}Fk9O9xe9gX^dJUv{t^kise-n)>dwUkWdB;B7Q2aU=f605@ z)1afnb)hks*-r1+^Pg5Kk&@->g2`zwd|b5|<R|r1c_5KU=(`VT_P!qb(2sYYs9x~O zV|eWCBi>&>9a=PLomJWI-Trj2+&g~QyS+UAwAJnO8hlMJ@|7PiZw@&7MUU;#?RP71 z2RWzFnLqp8lh^&CZ{TRQyn^!?5Vw)De<H;uG?~4y+avZzV)ww6a{_1u4nWN$+V$d^ z`l1dy#nCRV=!)1!BlcSYI9j>K;qvC4%AE_{b@CQC=qupJdhh%I&;IO36i(ixw{Yk) zn6cP<c=|9}y#FF4=jsm^f9u0&!lj?_JW?90cLExRP1;>=wS|hyYV{%;-yg9b!LyjJ z4Ndj(lUSJ)LGGBo3wl^qtnp3X^zqEy5-x7%lJtV)-%eZ~JN?hoDpwOciZsP&1<G!F z{0r;}m)%`Wx`10h^#%69FR+_mU>_0o^+wK2Wiu?6&1WnY&kqf=L@I5tq>&zGsf=ah z)<@H9(Adb#{JPtWn8ij?aVyCJ{y>`+xHK3Dw6P{rTjj`TP3By-JerFR8PL+~n9Dq^ zrq<dj+G;liTH9{%ESbx4mT5&T$ogZ^SkhptVjb~BM{-H3W3a>Q80la!$00S`AIoMk zRxTPFWDAFL+1SF2VJ+m1TUb+`H8nNm{Y`mu6XV5neiK`oGSYEvZIgMCmd^}kvLhLI zwCc_dZ_K3zl9txoy+CUV1TNLqZPauK6?|74Y0J<$)6qmWH(+!PWb#AV%%JIq+G2bx zgVdKynHuyz0Atgjn8+E1W@Z!CNHk|?=~T?fm_{5rj2k)as%uwjS1pgMZ<{-kvXWZp z$`#i|R`&HqZq%;5PFo$i;fBz)eK-2&qO_-V70i!V1&?QW4(5DjHnRzBxznYH0{vs) z&sR(9<k@1;NYpe4ejUUBZRPU8nn1faw8QnaJf0rNAZF7-uju+{Dji*yHZ)v#b0MrX zH1M=0z#oa$iJyx-D`3H(Nh$_|M{H%aylKe5JgrIa$6e#`@VUaGHe{G)bO4<^ttqg} zh_p^9tp`oRnu!txFG75gW*V^QaSR*U4MIDNNgHgz|NMTxrzd3&r=uISY$m-?<2G~H zh@}!KBYrZT5opKvs+MWN|FGd|+`AgAvSz@xgK@x4ku7kQ|DXQXX8^yaF;%gu4fXTR zsGSfOD?ZBc$2o3g?0bS=Q&(TWm!?kfC(P^ud_RJ8WnKOE=k(S!+*{pSr(Idse9vj6 zns8kpRM&o0-Nb2yGwy=dDW0zC?(Wc<;p8vSF7aRLZ{v4NFcAEX)_en840_aZc<bf< zMC=lH9rc?Vhpgy2$gP}{CuJ#Tq@#Yz*kCa~FUF%*l=;`0Ce*W7!8SpE@YHI%@OE58 z#Ypxi@H%GxOx7~|5ML`?o^YA^W7(l01Md9)PSQXIr&LadRP7RlU(`zC<mBfD4!#bb zHK)4p+MxyqHsFs1ki77R{YW`sX82-_99|qwO&+JJiS?F7O%_CcA>_05&-f!4V+VvE z_K$tI`wt4{?q3tv%LdoPXy8v1{;Gi?wowRW+5RmTm&1{HFW6^mUyE-VkQ#B5lO*Gp z?QaAdmR)YFnZFA$_Kb8C8nV5V?1RE=^LIeZ-<s1LPn<t&E8ENUO8}d#|08uyyA4h@ z^C{a)5QV~Q{RePkKnge+-#@q{vc2qo7?^8_yhRcNo+Y_`N5RL<u$gR+`-3L8FFX%_ ze(wVNvFP8XjiKyrk9GIjVUwh|5GTaO#B(Rt0PS-8cud@T7wyN+#ZShLIN8jnyZ;%3 ztAKtr?4K1E=GE%Yjl%J&o;4DTPi!KWSoP;_9ma5ttB8(90<LD_awj@CCv!+V^7{dQ zUa2De){1ldHNiDeUqiI2XLc8XP^{)royxFQ&BW#AGF3Bi_z3?dlyME+=|TXraleA& zZ;rG1U!mY#z|V%67!~s<zaR15nN4Szg5y0in}15dhZTIIf{!Zr4;B0;3cf?ZA6D?+ zD>(jiG~0OhD)_Ssen7$BP;i#c;7gJ}#<Ihf$@2IZ&Jx4^0JD<t;9y$to{|HA?;8o0 z7|Eq9wCAlDGd83wHWW=|z$=%FC39@Rut23hoy`mg-F`C`&Gg4pIh2p)23UVTILGtD z*v85ZI&~|XPxtd@{tUcCn%E{G1^BDxP&A#+#+aEAaX{m=f&Ip_d5ec*O8@*YJop&Q zOdH0qWQkNJ-p?Q6XKnzz1Mw`w&j>uplZE#I-X87nhl&4e3c2JxoHB;z8WZZ^x=Ee$ z5khk72OP|G=&ge13lsX|-7a<TITMpx=Nbpk=yZwta*?}rRufLgn-E-%YlWx}5`P*` zj&Q28mFVEO?i2O9h(FaS5l(e}COF1}I?ssugCcj2>mkCa&L0GqI`XQ1g80*T_7F~W zUUKOK#C`TE@uxa?zA(Ah>#&1o)@!ZMc~5ZnxIQMF##1ZW$#Kd1^-RItI`avqI^S~X z@E<*)&t~FJ$F-1fs<W8r;CWdq;_fE?ROf2Ksm_f=2iN_GsNYKbsg6N7)yWcmHt5K6 zw4U%U6Fx~eo^k2_hT!sCjDQW(hl1l{GLCmMLP#|@@sQA;J{NfiNB_Ig3DSJQF~fiV zll@*SIO^k{OqaTJ<oUXW_@n;)qCt%K(|D4E(|An5F{ANpbNT;D=$DB9xxi%nQ-n7V z{v6?7CH!@l{^LUb@5CSPSgHS!;5aw<{4T#M&k%z|Grk*2{|f|{>nQd8#GmRf6<q4$ z7%&YGe>$$TOCP_#F`2}_5$ZCYEre5@F~aHm{J^Cz<NpQm$9a(Yzft(_5?qc;zR&De z=)6EUou6Zb)A*|~2uL{pG=3l9)PEu2biH~Br|0@=!tozK<ah@N$D-sr1efCl6HiAK z{-@(%fQ0)5UF36mqu@BM^WabFw-ZkLy_V=;u?&Bh;)2U~0;fAX?cxrFdqQyR7oXu} zzdH!Wetkk`FX7bxu;6n4KPx);SmA$`(8GQ;_>=v%6OR2hiuzK*X}`A-PUmO6i%Xp! z2`=ZSS=1j@_%FaO2}l?xji-(9^T8(Lj|lG8zn=JCK>TkdoPLkQ38($$2>%w**+4j* zhb@HDIPVc$uGgPH3)B6A%lIA2*uzAJ&i^jL>2v8`!f8CO5KhN?kZ>P#D(ACRTp%(Y zd7oV7;xf*~g5!EM5uFs_G@eny={>N`r7x<iBnoog{vtZwPITxz{G4$5JRsx6uX%Aq zIMReca@lW}i;JJ1*jg9IJ}`~)Pv`d-)W<s!(*ykTB<KIg3#{zo7mE6uE`G77GYk-t zjI+(D&U_-rHJBFnxEzP%*P{`V{9bi)8Lyjbyp_{0N=Th%1rI2AyMlKqxUS&K6?~0? zuT}7*f)6YB1_d8g@NEh{q2Tu^_yY?5kb*y=;5!w(tl-Zm_=^gDK*8Ts@IwlIM8S_K zI6W71f6DhM_kONN0Ljf86kK!h^YJo+B;Ti`&V0e;d!*#@ytKP?J{0G)%f+R??&9)X zFL&{SLT8POOP#eYzDf8eU0nJPEBFQlA64*e3O=FW_bK=TF5WNVf5^pU{EsO3P6aQ! V_+D{OYJ8PX?h{dET`n%i|1V-jQW^jN literal 0 HcmV?d00001 diff --git a/misc/lnstat_util.c b/misc/lnstat_util.c new file mode 100644 index 0000000..6ff3779 --- /dev/null +++ b/misc/lnstat_util.c @@ -0,0 +1,324 @@ +/* lnstat.c: Unified linux network statistics + * + * Copyright (C) 2004 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * Based on original concept and ideas from predecessor rtstat.c: + * + * Copyright 2001 by Robert Olsson <robert.olsson@its.uu.se> + * Uppsala University, Sweden + * + * 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. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <limits.h> +#include <time.h> + +#include <sys/time.h> +#include <sys/types.h> + +#include "lnstat.h" + +/* size of temp buffer used to read lines from procfiles */ +#define FGETS_BUF_SIZE 1024 + + +#define RTSTAT_COMPAT_LINE "entries in_hit in_slow_tot in_no_route in_brd in_martian_dst in_martian_src out_hit out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n" + +/* Read (and summarize for SMP) the different stats vars. */ +static int scan_lines(struct lnstat_file *lf, int i) +{ + int j, num_lines = 0; + + for (j = 0; j < lf->num_fields; j++) + lf->fields[j].values[i] = 0; + + while(!feof(lf->fp)) { + char buf[FGETS_BUF_SIZE]; + char *ptr = buf; + + num_lines++; + + fgets(buf, sizeof(buf)-1, lf->fp); + gettimeofday(&lf->last_read, NULL); + + for (j = 0; j < lf->num_fields; j++) + lf->fields[j].values[i] = strtoul(ptr, &ptr, 16); + } + return num_lines; +} + +static int time_after(struct timeval *last, + struct timeval *tout, + struct timeval *now) +{ + if (now->tv_sec > last->tv_sec + tout->tv_sec) + return 1; + + if (now->tv_sec == last->tv_sec + tout->tv_sec) { + if (now->tv_usec > last->tv_usec + tout->tv_usec) + return 1; + } + + return 0; +} + +int lnstat_update(struct lnstat_file *lnstat_files) +{ + struct lnstat_file *lf; + char buf[FGETS_BUF_SIZE]; + struct timeval tv; + + gettimeofday(&tv, NULL); + + for (lf = lnstat_files; lf; lf = lf->next) { + if (time_after(&lf->last_read, &lf->interval, &tv)) { + int i; + struct lnstat_field *lfi; + + rewind(lf->fp); + if (!lf->compat) { + /* skip first line */ + fgets(buf, sizeof(buf)-1, lf->fp); + } + scan_lines(lf, 1); + + for (i = 0, lfi = &lf->fields[i]; + i < lf->num_fields; i++, lfi = &lf->fields[i]) { + if (i == 0) + lfi->result = lfi->values[1]; + else + lfi->result = (lfi->values[1]-lfi->values[0]) + / lf->interval.tv_sec; + } + + rewind(lf->fp); + fgets(buf, sizeof(buf)-1, lf->fp); + scan_lines(lf, 0); + } + } + + return 0; +} + +/* scan first template line and fill in per-field data structures */ +static int __lnstat_scan_fields(struct lnstat_file *lf, char *buf) +{ + char *tok; + int i; + + tok = strtok(buf, " \t\n"); + for (i = 0; i < LNSTAT_MAX_FIELDS_PER_LINE; i++) { + lf->fields[i].file = lf; + strncpy(lf->fields[i].name, tok, LNSTAT_MAX_FIELD_NAME_LEN); + /* has to be null-terminate since we initialize to zero + * and field size is NAME_LEN + 1 */ + tok = strtok(NULL, " \t\n"); + if (!tok) { + lf->num_fields = i+1; + return 0; + } + } + return 0; +} + +static int lnstat_scan_fields(struct lnstat_file *lf) +{ + char buf[FGETS_BUF_SIZE]; + + rewind(lf->fp); + fgets(buf, sizeof(buf)-1, lf->fp); + + return __lnstat_scan_fields(lf, buf); +} + +/* fake function emulating lnstat_scan_fields() for old kernels */ +static int lnstat_scan_compat_rtstat_fields(struct lnstat_file *lf) +{ + char buf[FGETS_BUF_SIZE]; + + strncpy(buf, RTSTAT_COMPAT_LINE, sizeof(buf)-1); + + return __lnstat_scan_fields(lf, buf); +} + +/* find out whether string 'name; is in given string array */ +static int name_in_array(const int num, const char **arr, const char *name) +{ + int i; + for (i = 0; i < num; i++) { + if (!strcmp(arr[i], name)) + return 1; + } + return 0; +} + +/* allocate lnstat_file and open given file */ +static struct lnstat_file *alloc_and_open(const char *path, const char *file) +{ + struct lnstat_file *lf; + + /* allocate */ + lf = malloc(sizeof(*lf)); + if (!lf) + return NULL; + + /* initialize */ + memset(lf, 0, sizeof(*lf)); + + /* de->d_name is guaranteed to be <= NAME_MAX */ + strcpy(lf->basename, file); + strcpy(lf->path, path); + strcat(lf->path, "/"); + strcat(lf->path, lf->basename); + + /* initialize to default */ + lf->interval.tv_sec = 1; + + /* open */ + lf->fp = fopen(lf->path, "r"); + if (!lf->fp) { + free(lf); + return NULL; + } + + return lf; +} + + +/* lnstat_scan_dir - find and parse all available statistics files/fields */ +struct lnstat_file *lnstat_scan_dir(const char *path, const int num_req_files, + const char **req_files) +{ + DIR *dir; + struct lnstat_file *lnstat_files = NULL; + struct dirent *de; + + if (!path) + path = PROC_NET_STAT; + + dir = opendir(path); + if (!dir) { + struct lnstat_file *lf; + /* Old kernel, before /proc/net/stat was introduced */ + fprintf(stderr, "Your kernel doesn't have lnstat support. "); + + /* we only support rtstat, not multiple files */ + if (num_req_files >= 2) { + fputc('\n', stderr); + return NULL; + } + + /* we really only accept rt_cache */ + if (num_req_files && !name_in_array(num_req_files, + req_files, "rt_cache")) { + fputc('\n', stderr); + return NULL; + } + + fprintf(stderr, "Fallback to old rtstat-only operation\n"); + + lf = alloc_and_open("/proc/net", "rt_cache_stat"); + if (!lf) + return NULL; + lf->compat = 1; + strncpy(lf->basename, "rt_cache", sizeof(lf->basename)); + + /* FIXME: support for old files */ + if (lnstat_scan_compat_rtstat_fields(lf) < 0) + return NULL; + + lf->next = lnstat_files; + lnstat_files = lf; + return lnstat_files; + } + + while ((de = readdir(dir))) { + struct lnstat_file *lf; + + if (de->d_type != DT_REG) + continue; + + if (num_req_files && !name_in_array(num_req_files, + req_files, de->d_name)) + continue; + + lf = alloc_and_open(path, de->d_name); + if (!lf) + return NULL; + + /* fill in field structure */ + if (lnstat_scan_fields(lf) < 0) + return NULL; + + /* prepend to global list */ + lf->next = lnstat_files; + lnstat_files = lf; + } + closedir(dir); + + return lnstat_files; +} + +int lnstat_dump(FILE *outfd, struct lnstat_file *lnstat_files) +{ + struct lnstat_file *lf; + + for (lf = lnstat_files; lf; lf = lf->next) { + int i; + + fprintf(outfd, "%s:\n", lf->path); + + for (i = 0; i < lf->num_fields; i++) + fprintf(outfd, "\t%2u: %s\n", i+1, lf->fields[i].name); + + } + return 0; +} + +struct lnstat_field *lnstat_find_field(struct lnstat_file *lnstat_files, + const char *name) +{ + struct lnstat_file *lf; + struct lnstat_field *ret = NULL; + const char *colon = strchr(name, ':'); + char *file; + const char *field; + + if (colon) { + file = strndup(name, colon-name); + field = colon+1; + } else { + file = NULL; + field = name; + } + + for (lf = lnstat_files; lf; lf = lf->next) { + int i; + + if (file && strcmp(file, lf->basename)) + continue; + + for (i = 0; i < lf->num_fields; i++) { + if (!strcmp(field, lf->fields[i].name)) { + ret = &lf->fields[i]; + goto out; + } + } + } +out: + if (file) + free(file); + + return ret; +} diff --git a/misc/lnstat_util.o b/misc/lnstat_util.o new file mode 100644 index 0000000000000000000000000000000000000000..b9de7849ddb70e343f21f46a5b3b417bc3057f7b GIT binary patch literal 6688 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 W!dgQ=KZo52NVxbe_xo`1{rxZZ4%<us literal 0 HcmV?d00001 diff --git a/misc/netbug b/misc/netbug new file mode 100755 index 0000000..6d13c8e --- /dev/null +++ b/misc/netbug @@ -0,0 +1,53 @@ +#! /bin/bash + +echo -n "Send network configuration summary to [ENTER means kuznet@ms2.inr.ac.ru] " +IFS="" read mail || exit 1 +[ -z "$mail" ] && mail=kuznet@ms2.inr.ac.ru + + +netbug="" +while [ "$netbug" = "" ]; do + netbug=`echo netbug.$$.$RANDOM` + if [ -e /tmp/$netbug ]; then + netbug="" + fi +done + +tmppath=/tmp/$netbug + +trap "rm -rf $tmppath $tmppath.tar.gz" 0 SIGINT + +mkdir $tmppath +mkdir $tmppath/net + +cat /proc/slabinfo > $tmppath/slabinfo +cat /proc/net/netstat > $tmppath/net/netstat +cat /proc/net/unix > $tmppath/net/unix +cat /proc/net/packet > $tmppath/net/packet +cat /proc/net/netlink > $tmppath/net/netlink +cat /proc/net/psched > $tmppath/net/psched +cat /proc/net/softnet_stat > $tmppath/net/softnet_stat +cat /proc/net/sockstat > $tmppath/net/sockstat +cat /proc/net/tcp > $tmppath/net/tcp +cat /proc/net/udp > $tmppath/net/udp +cat /proc/net/raw > $tmppath/net/raw +cat /proc/net/snmp > $tmppath/net/snmp + +ss -aioem -D $tmppath/tcpdiag + +if [ -e /proc/net/tcp6 ]; then + cat /proc/net/sockstat6 > $tmppath/net/sockstat6 + cat /proc/net/tcp6 > $tmppath/net/tcp6 + cat /proc/net/udp6 > $tmppath/net/udp6 + cat /proc/net/raw6 > $tmppath/net/raw6 + cat /proc/net/snmp6 > $tmppath/net/snmp6 +fi + +cd /tmp +tar c $netbug | gzip -9c > $netbug.tar.gz + +uuencode $netbug.tar.gz $netbug.tar.gz | mail -s $netbug "$mail" + +echo "Sending to <$mail>; subject is $netbug" + +exit 0 diff --git a/misc/nstat b/misc/nstat new file mode 100755 index 0000000000000000000000000000000000000000..acc6870b818a5072a16438a1c97940c8a5e8f71d GIT binary patch literal 22421 zcmd^ne|%KcnfFP;kbogGAYeq$0TVYt2niqpH97+c+^_>g2>64_5R#c>WHK|IAAlAy zHi>0A1X9<&^=-R0wXRjyTDJS?*0OFAh!C*5HKMy>sY>gwdksj07E*sO`+d$mC)|4h zeLwqoKkq;9$>+{<p6_|ibDs0-Ip_Y!d9c#6Br_v}$&krzWyEz%aB)cgWo&RhX9YH& z<uDhU!X~kiKy0{NJcXzple9;umNZAmWr2>Mc%}d{U3x^&$vVd*l@=1E@-76Ii0bt9 z**YsJV-5~bKC<<wNIyEen&Xn@aAO!N9agUABl9R9o5RIH^?Z_c3d1Co^&@q3O&0Z= zoaq8byo(5P^4|+o6wj*PkJnSYr$iU%DRJ?Lq|fOvlT_xr9r?JOBK@m~v~yN4uyNkp zS;6|5!9Xanb>`Oj^JdPQTM`SG%;xq}D^L}eu2{oWQgvjR1Qg=Bl_ChY=!E~yG{h-d zj?0egYGM#>)(N9wMNK;w7gc!*t}<MsaD5FIg;HEaxa3$^Kp?_GTvy_nhie+HYjI7- zbtA4Bxc*N9jl*nQq8oXZF<+4}59u^a#^Iv2pf;j*8IOy?1Y8q&O#itC<T_mWxTfNw ze#*r~;Rak|d5p2E1SL{{Yc#H6Tvy<_9@pRCl7ae<hO!gaEL_wNlW^UH>&wE=QBks6 zhBG2kY{4r9{t2j2UfQtdGWfSG_%5NR!Ggaf@Fy(z2x>@#wHAARp~LJyL5=pb3cST4 z|A@eEvfyn3{|^f;>$S~-F91*B5H4f6{~+*tLC2zJkI$>-a&lf#pBd$M@KlUbi+)*d zw*}XPe3wO!l)u^{UnAt-v&hT#f7F5>Mt%y<;3E4~i~Z|?8|_b$Faqf_;+uq?brySQ zK2o^dg3G+$u;4UrDXhgMWr&yk`-%mp{-dxGmr;HdM;ZII1%FN8C6;n!`;4>Tlf*cD z-=e2e=;^e`lh2_r-hxZ}msxP>V@6o;gSr|P0yWlalfch`QhyDK{u&|hgBCqcfTwVU z1()Ne8EHoS<ZCGW0hbXczfHk~%ZR@~Ai^US{RecIod7k;%W^kb<YoJR--0g_dd^=) z&tVH55%S-$=(z<vh1V_kD1nz-@Dg2|{mg>P_OT<~ST8v*6yVe^Lza2;LyNrBL+h3k zmo-p3Ijtb^Sf`t4S)Rv&;Rfd08e!gLo4u=i4S`tP7hN2zjm3O1=Jhr-g+tz0yfzy5 zdMQ(#o@q;MARY<Svsk=7oQSh%ZKyun#OlJKkgqPzVxdSh5Q_U*gD>t2ZHAgyJQ@!N zy#RgDP;HPBD7!yaR~zy(KiC+H_@dEpl#&uiYN`ze!*whiLD>x~7Ovami!;AJn20qp ze>|E9)z!v*%wHD_$9$}IV>lXTwRLsA2uhDfqjil@<`+h6420@gU1NP9O4^9=L-QsU z3p9`^i4e9bo0z{o9Px#8Q|l8E)D;a8V*YRxlDe5tOr?^A@j#O=?60rAj~HlW1h7ar z7^LFsnj$PFN=FN!UL;0!AW{F8XaJ^1eOtIKp{Ck+T_d_65J&0$t!Qk>1cJU$7^Ukr zQNxQ~XsQi_s0^g@qEOKobW>wSeLl9-Q?+QZcXr9_625e2VkdF=zpO7wyL_%MOVKr6 z7;qVk{`5VK6Era?Fp9A(_9lS{luG+8+VfD*(=&(DJLl@_AnkFa?;tt3KH3ncIV6Kz zcL+D`f24mSyjTi@%K1e&?f+!x5#?qB5T^2^Lk3*9bdL8MaM}aPaMXZH-%4r64LFr8 zgKEI3&N2)daI#5;Ap<V=3Owy5z1>I_4L%%l47l_il;|+vwAYX!-+;?KCZ#zIxLkV( zFEZeH1c*>-z-3=k(tHCh_pOAx47kxhDh8Z<o($CnT<)7G?M?$u`)C<97;xj>y3v5s z{#u5J0hjk2l(yA?(|%coW&=+9bs2UTaC!ehX&nZf?pb7b%z)GVhzy+uobCl=c-nwF zB?!95fKN5xFBovS$D-sO15Wn}G8{7C1rh}9H{gW^{HOt^dlMOs8*o~qWKa!wkpw{p z4fu2eK4ie<o{1-mb(6-w@!l=RfE({A9R^&k*`z1mfY0KJ^&h7JFE!vr2Au9YWhgb^ zvn2>R-+<3C;4T9`*MKVq+<0$RZNSN&%5bLvzgdEy8w@zzGt1Cuz~y~0r9}+5ypJYa zX*rRj_QK<-)8O9Jx8QS?mX~sR(>Faa&B4-zPvW-&Azes(BW2N!rx6N&Ks>ott)KIc z5Kk^u>*4%&h$mO7?cw~lh$k1Sb#nfG;>mSt9h{F6PcBny=KQ_HQ}=5T&Tk~1T%@*v z^LG<Zu2HMz{3_zfC2B6tFC(5@p;pTIMZ}W})SR4ONIbbd&B6KE#FNX@80V)GPfL?F zcpgCEG~&tiX~#J~iFk5(T0iH<5l^m8>*4%p;>pEndpMs-Jh?Wllk=ZW1y3$b>)`xp z;>ne1&7A*;cv=Fr2<QJqJh?P&1LuEFJh?Khn)649Cl{u<IR7&7<hry{&hIClT$bkK z{9fY8RcQ{+|B`reQ5xg?v&55Y(gwev`aeOullbGD{{ivjinM;tKSDgYAgzb<-yxn{ zkG6;N-y)t|j@HTf`-vx4qjhjTN<6t3t(o)p5>Kv0i*SA;@#Ipp4V=H5c-jhR)tp~N zJh>Ll#rb8#lS|P`IlqW_awVFR^9zY57os^hKbv@R9U9~ObmGZnXoH{g{wJPXg?60t zlZYo5q4jfq9P#8Dv>winCZ1e^wukeX#FHz~IywKT6Fj*9t%LKYiKnTrHG>D`K`A>~ zm3(!*lKhj>^4?%|O;uk{XR(VZefwOT^f{)vn7ST0{@g)xSxMR6yw<^#ww`!qx}Q&F zn!dxKx%_I_!PY3r_v4O%0_5q*QIfXrq44w(WZsF0l5!|*M-uNHw4DT(ak$s-_jl-5 z#~P)j{FTYjKnf=EyFMx_w|fW=P^*%?hu!IS5BCli*P$f8UNER6g9Ss%Lnj_Xm6YUP zl;peW=bxq1l(9fbzEgI#tVjJ<G`f=dQNa$9Nj+E4LG+=QC`)hmx2C(0@m=*Ga{Xo} zF>O6nNma2|9@_f|*sce;<`1Fgr)Y|SXSu<!xufebBzHfFAMG3H#g49D5#L38E$9E8 z`2QmQcH+@6U#u=WMomdNI+VIzC3#9o%{|Zw532g09acNiy)B=O+>AbXDW|Mw-95b& z*^`0Dnit3=k`hvpn(!Q2u<b~^wCvb|*B&T>KJ?FZXyUrpl;p=s>X!bpBkCSh9ZIiE z_kL~4Hpp-LeOV7FggUNm;IwFbSma(?S$5XFrYiY`yJpR5w5h|1;@Y1paG*~-?ezr? zPx7NK#yjYg`jp<v_SRp*sOA^B(DnAVqufM|w5BqYmVG1IJJRRRpFh_>b^k+uYDS2w z$^Lz+<J|t{W^B(Z`jvKfred!+#EvK}Z)PdQT66hq)Pfys&9#HD_%x+Gk<q>3W;D?v zl)0B0p{>VWF<~dfk`o@LDnnu~^%EC6X!``il&KY;rqf1?{|p|TYj0bD-dNstHhvvi z><(o2B+o2Q%~O(DO7bZ1#3>~?<u+3L&`Z<=>+W%X-F=UHeXpmzq5yrOB>(71{;BJI zbS(N$odTm;hBCI_-7=K<U^XQ~%9H$1&4sU{;tqm#7r{C;`d4m&lI)GoAc<*YQF1Y6 z)`$X6a!yt9ZE*Rx97+mG5OHcdkXubcu)}?a=poTPMX=3-?m@SLZYyvq$!!JsN~)$H zpZAgn#xYUnR(kl;MRzfTex#$YLrEU6w;lm}kTG}i4|~&Cl|0FhJ<0UJiUKFAEIy&M z^k%Am9ZIL0AIN0()=AJ)Rriw82m44^DdvV&6gUPJ=#^}TESi}rxea>Ma#R|Xqk2*W z`xe7&jF<;#C=b|M4}<9<2(wkaT^a_f9szc+5_tN6`=QUNtSygIvpAIG+uXhpPR!<- zf*f@nO4Q39J6v|6Rjr}cPJJ^+{a4Zo?f(o){a!3OoYETjtV_Isq2QT<E~h~-Wf5XD z5Z=HDpjMV6!0qkrqHO>vdrJ1@0ejmT3@Ggm{FoXarKruiLH5=VQqgY9+v{za16T6- zj}|UuRSQnoTNLy$Zv*O_2QDaXoliR3w`M5qtMk!AU8gYS;E0}u*7PBJ+ts|t#rfT5 zfd7Uz0H1r3=V|<$c`Dwiq^=q0H2Bxm>fIQsUD-FfSh7#uJls_Ai3<3~$w?;PS*Mks zOQ*Sg>x|AB$ZB=EA^$u}{w}2P{9l~CZ2p(g0kZtFC`JquUjDVR{C~S#e)k=F$qSUh zJ*VAk-D~(XfG6~%Zu;NXz!k&`Ms$rr<<w))prmGQ12`~3Nj1TxpR~6=iRMX7_`6A% z@2Mt7+Y$St`^$QkBjwSaw*B$3e*3ser{7*;`+JPE`!nNxO6q|ebq6eN>q%5%+>L-A z{Ut^Px;1{2C*{Me{U+x52kLVu4r!Qq*C=hr;!`C4D9013C$-7pNzEA;L78|lo}@+| zBoD@7By&fs-T-NBC3R>Exo6$wV89vt^BrY9yajkIEiLeU)JsX;QlP&57fiQQ;e+6p zx4jn+RF=`0U!Dq*`#uxDp$ajv{=j~$6zU1&fS+pZppnh%ka#aO=H9EZx*Rq)(oDbG zJ`R3?nrSokv-eL<Oj@2QC_q7p?_2Hes7h_a<XnQ`jMh0?MN<{4QbA=~`h}^ex%&kt zeuYMly|M?b(lry1-X0TS)xZ-<YO%w!K;1mbO>Zr*3pjw5tSUaBtwT#)7~8am=s05; zuke=_4U|UIO6jxR142P<#HdrRr=Pds2&i{cGb@njN#0Zr5--I7@gz^iomHvz7-;Bp zay}obFZ`L;$|*)y;yo-N<T3cR$k2OAsuKQuf7Oj^^Q)3e@~bk|I+VbE756T*67BdN zY&fmhr}vz8+o!*gvi<QY2Rm(d9C9z{J8fV8^0xKVJWHIX?O#81+J4uVC-N!z&V7&` zyTn<w;ADKNCpoa3MmYJh*U)6-ntlt4#KgN~Fs#LRXX?N3#8c|?pfFaWMbQ0F`a_tq z{Z|!VE8mEsw_Qz|S2zcrR9;neyX<%NXkLh(CL`^4O}LJmu)g19*vp!qw5@d~&ks+_ zQY=O}ek>Dt8CacY%psmjacm^iLubg;UbPwRm>ToJL<gqkd-(0zFavugn&zIm!=AeR z`~G^}^1$KOmTz?I`xLPridF5?oK^c&r+eRDN4ob7jdT|uR+4YJi~Ez9(CT(*RKE*n zq<ZKl8$V^3vmdGlkf5aESRrqW7ZfRbUpcq>-GBJuOXxxm)|4APw0;gM=RTs}z+v7+ z`-W~)_I;YI6z@;jmcY99$zvDvK5$3-_$&qMZB7>rN%iNS&`{W(?LJ#MG9%$l+2%r$ zh9*XRo6-)i#J4QnXrZ*a2lYnB?+SoP*+xS$eJE+m1RdzdZuOYDl-!`c%3-2W4CAMw zpeSWKFabiXWk<@+RwU1<OL?iu16y~ul<!B%hzG~2y@PyEj^92qZTkf<EGC^OPou(; zr@9tlZmAERhJIR|55y<59owFb;qp30e%V>t%KQM{t}LxygG{g=yUe1!V^Kw3Po*8r zL_45d>PNcSRbS&aB(ht!735yA#qk2w5MH1N^N{AF+5$b8_2U=pkB@3Q!jlv4@ckzj zp{@H-PqMz?I5zGcFe>fn``t+_V)24O4+hXsW!Xt=L=T~%{T+KNPysls3UuZWif$Q7 zCwuL!^ct*v@v|*M1zW}>d(s659-9fbLiVS|1jakq-ubc$-5nTQ>T<MFH<_x=L0k0M z9)JvW<iL-+j$$9)y$YpZ9QjcK+QJSQ;ewS^`Bccl36H?|>`G8`Iw6%ll*mijrXdME zyL&AFlzi$+luWmvyz#pGspQvA!KJnK**-%Wm8eaJY@cmNve$6-kCM&hn#z9;zU=T` z+IN(lRj)&VfgT#2N!vdoMf(7I2fe-fY|qN9uL4x+Z1;orw7*!;iM;;4R!%dpvo3f7 z09)R^CCgp>lF}YLz|=ROw=04wCHH%h=TQH708+MUXrS?0276PsTXn9L-eBrRU^%nJ zbI6!!NK?n6hVT?)t_A~d0jD+HHj2mBBktIzZh%(g8KWRi*%9v3c>^KwRd~F9c)FKQ z+sBT1fwW!uF^%Wsf$pua%f2IpO2anw-vPH*XJMy<A^RX7^Y$G9YLQc2Hq=Y~A<x(S zFweCH3F<Q^`Lt`h5$)3bD0SS=fS`MuX)vHROx;W|dd8#%5mT>%4!!H>Iga`mD%Q2( zTKFcK5NZ-E3Lj0*C9S=IIg2jXb4#Bsf1HDVo1(o%CH*gyq$Fo923V0S{2WB}qFi$Q zt@I(3(*C0Y2PhX*E0EOH4E0zr;KtDhvtdL<s_@uY2div*&)&8L_N8VX0^4>n?x;vj z`4vw&W^c<TZIowkGh$tf@bX4|NpFTHc{4mi?bFxwZ&dehA^|pFG41Z5#MYfiq&?ub zsM6F1h_6Kz+j_c>kwDuf2+-cNmU6b&6gZLx^;UCoDSKNbX@DPaW+bjqQverK6wHV( z#zgo4caM8lK&RfCLo^XyJ^`<^6>dj1fO;pgbo~Wo$*TVvCGn?a6{!htKt&}sY^|@M zZe+$Pj3R8<e~8*+G4BAOfM*TgMYQaQUJ_)P+CQKVp{-3-?Otr{-%)p=?BUTfm5+-! z?)~1u^!O=qgz$=urpnI{kC$SqJc9^R?=<Eb0E11T9hMyB`$K!{XAo*TxiuTmOlqT+ zavNC{pW0krPq)wU{Li~+RqM8){=cC|k7u5;x4i`$)%R(^>ZG4__-QVmOY(_HeYPS* zHsO`1zt46(qQ2lk+qINdk+SW>t#KtxxFJ=(7tywKA`eAALaFw)zlB!#sx~CRVH||g z)J;1{>a4wucJS1KkD&@>XVJ~O(Le-bgYtGG<`Lu}I-8`@+BsB`nyweDwhe>5JC}N~ z6<L6u1gdZ9PpMN8$3UW${dx2#Y&wsE8I9F3Zy{ER9c9~#s6DBB{UfNG1t-UIcjuGN zQhj>R&0{GIH0-zfx(bnk*)SgRO5c8DfXl@r1D^VhlB8R}9h8RgmjgfXdsv96T?q5| z{h^XoQ_!d^=(q3eK?;WC>O2fddK{<^6b0*rdNUMIl^;Yd-Y}cVy4^fO`9fq+A3&pM zza+<+8Z*|cAwnAdq9LizHkvAopy@YfV?G#TDvXQ1tc$(_EffygToltjp{6aSW++DC z-BeHQ4s4&rBQIJVRa!5HJne<w9!2~8ARd6VoXE$%VYiCX+ielRp5o)mzM(8-L2t~C zb>5vG{|6=At1K9Z{#i*)E>P$Oz>~r*3~P11;r?O`@|Tj@_L;>*iq+LzPy3x{+7meg z+3A|Ma5w&2rRB49;*7rC*KULT!ajQNw_54zrSBXm17_$SbFgP|$n~>jAP3J4Q&|dD z?!+ft0X@$nS+$#b3wJ)f^iDOCRYjJBqg!gD^?^_Wm<pf2HW7^1)Oc7;U1U`}><%`B z@us}7N$|_@;#*HyUb~fIAdB(E0Yw)lqR~oB>y4~>)yl=*RV!E4uvw94xQ-RZW);R* z$QPf5i@#gv5-Tceytr1^xNCS~EYuXy^HR#{70auK;dy3!9#j?vgNedm11k(B@T8<E zvN92O*Vjjv*Ty!j@<n1S%C0Yrjb?>2=gf{3&Mccp;`2(%{G*w(D7@Jh#RmvmiZ4#4 zJX=`fZJXELkFO+R^$X(*xwDEp6Y)SW5WjDRGk{7Z;=b84V=<B@R~9d<*Xcs1_)LPM z3!UN<hFNR{Rnx0ft*%+Q>MkiupB6Y9@s&e3dY_ZNH(=2lWt#pmggI3x<=;-2Q)b1R zBC|?(dPxGtkVEqYr7r&Y1@DVl34DHm&m;<i!v_1wezP_{UCciPE}b8|t+2iXZSIWK zHbwB&MuYxA$>!Q%d11YibbIj$N-PdeC|DD#ZSXC0a@(BioHI>r>%0e0wY#RKa@7h$ z>>lSmv=QS$R5}ns83DZrCoiHl6nEAKHV0ziC<zO>Sh&d-Z-imaKn$JX+!75Vz6FEW zRL2?4cq6J^<%~pqeqYoX3df!CFt~7?&sSdpg+ia~9%w^7>YeEN8P2E=AAI0DmWE5p zp+SQ_jFW~CCJz}BCw)Je=7N=Yk2lRl<o_DoaQ&tA&Akl%KP|uTEA&ki>DhwzOf%c{ z6AN#(<9i}K<C?6mJ;(HKZ#zE38icq5ZdV0$g3_M86f_^y1zHN)40<Q%aZuWsHN&yf z^Nk)XltZ8l>s>x}TrSZ0pv|Cn62&IsG0;w|qDMjZfDVGv{YEMFT7$SPp*Nt_xW(E5 z+KeT-AM`lrASeZT9!LS-(QI|(X6JNJc?k5Pj;`v7F8t!LD*wgzlX;yP4`;SyQNCMn z^^0^o595LG8eGROfd_#PT5x(H*NTf><)UAv|Kd4wp5x)nioE=mtR;C);3P-w>JoBm z5vRcAs`BzH@|-PHrMb9X$JHt1ek|&<D9^DgvobHgJ*zU$>CP*1=anwa>v6S=eAu>g z1mvkMc<{hnSGy3ycgtu;3dpx8FMn6oZe(^Y;`!Wp^OxowceP|cJaVV4oiZa(TX~Sb z`5G6o{99<Eu&;$wQF;*RJztW(lWd*r!q3I|$-d{vK9PQwOL6;lXL|DT7a6Rp$Qw3p zm*{3*zY~;yii_tQOJyOTek;Bt_ioV)e4NYxv*B77&nI!Rs{%|!;E@Y@smZ?5F6ysV z<Uf9$u2+6XEBg!e(tE2SZ-d@h?Yv5pk?%$1+cQ<qcMo_96?vtc&cN><knWh~;(Re} zrxBLzv^_lX?%kQYvRbm+M=Z;u`9NX3PFTkMt7s-<TqEWJl|$Flc=F1}+_IgSEhDP) zoDb_6DeV?qD$@D)4tzkaqJit-0~cu&XrD$w=Pu?YE}D<r1P_wZi&+Y}I$?ujT|7oN zo)q$@yEqlYk-0K;p6+!iJUz<A=}7*I|2xUjd0KlYye|s6LTB;is2(N=Dv4+=_VnCp z4xx#8C=vz9b`;G${0-fqTo;#<c(vehi_gPWK}QKH%g0taOprs!qX{`L%f)Rt=XYJ< z5;^}LkvV$(t)*Q%PPd~(&|3vvA?SKRLxOG>beEuy3;MjEU4s5j(6<HsSkUu==3S|m zd##`)g5E0V3PINk8WME7pt}TpT+rtQ?Gp5Ng1#;2$AX?0G;h2pU(gakZxwWfpz8$< z3A$a-U4lL?=<|Yh3Hm!h-xl;^LC*`CH$jvyXo;Y=3c5nj^@4^3-7YA*Oc=y{iZRR( z^K5~jO9Wjj=mtUW6*M7e=}o0`%E;L*UA%aqvk3pbRP3BnGPh*5b9QOz+_KWLo1I0g zut8I>t>gI2c_se3ImQ2<W+;i>*F-mCCHPQTr;RekKLL-h66|?>CGJI4Gvl=ltfa9P zpLLhi-xoqgoyMcAq#=|j!QE0U5Du9bFVdpEU@c`3u}Cn^N&+E#LtPU0ZN;x24AR2& zweeb3;%oH!qqVp{XskzG$+40;-2KE*pib_sLn3t6HU;WXU>IufQ^IxYdN#%&T^DX@ z@`d8YarHlq(+5`Mx}`s4nJ#b$0-*KD802~)vFi~bpDgoF&UAspnkN7!=Pvxn^+h6O zh>+`-`CXYVa184YC+tyCpv;$}!%XrwgQqn|=9lZzQL)hUi*<zvsgn8S`aq8sNL1#R z>)g{~A(87EM0l{)e<@H}Q)GU*j_eW(;Wm*^+AHNGtwB1iQIePIltV1kE|Fihk+fgH ztPT;<E%VEDv0CJp>!K`Q=9l$vvgDWR>>-g~uFI64s$|W-1sJu7+!x4w#o$<g2(lcV zGykt8VNqPh{5fJh${DBUXcqmba~JYE1b{C*O!?*i`F4nn#}2un5b`pg)YoFkFZV(D z({+hFDJZCHM+qkpA^T<f7fpBZq#}|3F-r#7eq0DY-^FFjFaIxA{=e*VQczHfI`(5r z{>UsBmyFEPGt^t!&szR7BEJ-q|357Muh`mN*8F=c`Bkyrsk8O+zbgN)E&1s)JPJc1 zzqLFo{~}~*>`D9O|1rz|Z|<bVMG$Uac>GGc4j_$0WPbVo9-Xw-B3R3lyreHf#+YC3 zYo8YR`$hR&nL(e-|C%L#Gu{qTXr9Lj+n|%-U*~X4kGu&&x}^QbMgHR=f03npr=D&2 z_ie;1B0Npzzrm7dVevm=7s?)1ipa?MI`^RwMBJL6csX~BqNOgK%eU*4)*%|wvfZR# zAvZ1Su1o5_%1WIsml;H&wR~e9Ydnjr&2(8|l$SaDfA7Y`ETp(HT}Ft<v*4lSx@1hu z!l;z%jTs-w<T_%;vxo0v%y<rye&39bV$z?R@zKNge`fp&CjGA&&mBHL!Hka?zHc<+ zc})62Gj7K@04^iM<5~FJQ~E7qVirc1^haiV9Fu;=j9&@Aq0b*Pjz!=S_gh)`&vrTA z&GHk6?@!J6RhXBT(tqLqrOonJ8`<G_7XQ!oVRkr@#b`HnameCN5X`tfZ*tj-mirHy zk0aP8!_Nt1E}B0XmuOE~rZVs!pw{-J>6?Mm2(0Z{$?>aLhov2=IF4C~_m33hJX?c! zCZ0MRx7fK!;LeFI#4d!(*{|<2#<Js<{%u88vfp~2rLU_F)<4^IK{;ajdU<iZ{t;>1 z&SJCtKH!&X&%?ko@N|N{=Aa;-OB~^NK68q8kn8m^;NwV8gfoJE3TniueJSLk0gd?I z=rFtPGWZ;hW7B4N{^tTt|Hn&TlTeWMtl;>?^WqMR{#~M6`oH*b)Q^_uY+JaVt5~OK z2jTYFw}rg*dFc1Kd_Eg*vF9m)y96%X_Aj^|VFbuqBHC-g<v9y)Snyv6dCh{;xS{X` zaB80%i#=ChQKI@<pG!{QxSp4O9}&@F;Ev2s(pG=9Q0TEf2loiv`aFA`MSq*nAF<$a zJkYbxv6)w~9hUj{U7?@;*@6Opwg~zYiynC%#IG#4JP+b9=|}s>eTD3ow}gJ{^W={> ze)0N3?-h*Wc?Y)&a|nyK5tsFvV8JyZU(E5`i=Nxg2Tpd<_YV}B`7fU5TJ%W$>nwPU zkpG4Sm*bxv*Bb4V=S}<oIF+l4_LuGRyhT1K^uJ`m<#`itSnz2=e!zlD8@{mM;&y{g zzyy~vq(YAK=@wi*Coi|)t3<iC1E+d9Ec0TWz>iz-O<d2#`-G@PKkauZJS_C&Tl73? zk(cfNv;~*vS-fb$JGpkeR}%UsTl62Z$loaBPYHQd$jg5G#3J7+<g;w%c9zf4uL6E4 zKQNuk=Q8@QCkku%FaGD7<gs4M`6XbsM94d)xi}=>Km0Anr4;{{3eI>f_$DE;o%CPA zFYN|S{W~beEvm`GPc3@nxf{Rcc<x2-Z+d_`u?|}IMZe|p7w->Wwdj}g@qOUj9!vY5 zw8)PZc4pwF&p6J@1wIit=^3)<xz-~8f{>pB{8H_+)FOXC$lt~B+>74B(P33adt|*f zTW~p^Q$kO_*#C&z9QGrDTi*jdWzn-t*z=+Vm*u_!{8HoUzkrX$X-@J!Rj6SngnZ6? z-G55_6RscQR=f|D<MtfKr4-)ZiRkETz5SWcBgfTv;Kufm=a|sA=KBguJKSuMm*<;Q z3H`a2dA-^qUn1;j0)DCfZMMkEarJjx|AqS(Y5x;k9{VfN4lbeppDcQ$pWiR!t?x@; z;rcH=SLI!c9%;`x;EqeouMu!iPGN(Q<O8Snw7$PC0)DA>xb-r6SRIZ(F&w_a>Z15~ zDdG2*)Zvhj#rTfL>%r$dIFrTetuS%R77r&yN=g}?RY!t2g{8iv45Ia6Z$mJ=u{P-C zPrSXgiLE#`gr1AnCz_h>ld3CMR9w(3nI)^-%PYN-qhe(If>!=GoonSEu)x^WE8Nwq z6?~0V*BA)aNBIdWmeWjJF5{skbl{2^siUJ?%#i+_lNk$!YwNx88?OuB0^vg)=B>DE zg?o9`VsYM#IbX~d#XAOXFdUAsM9dfT#bVx2qG==EJK$^_=u9*O@AKj<2ELHuU)A6x zg8tPMi`2&HJewGu?t*h}%zE_$Y|I!P5MxI4Gi%Hkoi;Q4WfMz8@ah2_v{9TGW0u8< zF?iA8t&K)&@ALX_woQXC<ckLCy!0`VmmeR4Hu2KuZS!;%FJkBv8?zey2pKbm;Svjb z8*#J`YVP&&UaN~Gc!A;&8*>KzFdj3eAAe)UFxDbo`bdl_g^J>(4GtkP%jn;(nKAv; z8#9Kk3;6HzV$?L?YbO>6`NQ5=I1#PG*+BA?Aaf3W7!gkRF{9$JATy|+NMy$J1BuL- zJnzVi@>7k>5Nd$01<e=@RWowotRORv56|d$B{Rs6J~Bi4X-sA;6!zldu?Ws;(p$&d z6o}~u59#L$iKCUwnfRAx!=qh41<5R-e<*0i^n;Jg81M4?ebI1C9BpJyz5^#S`9fw) zKUm3(nMOOlbi<)fhJndPlGlrObfQlBSxNkuC#xQM9ceg_%9>2743kPfH_9wTec5RB zOMwRN=NP>$6h`RtML%WAtU*oz)D(wdnbV;CV)f*AXwHb!x6C=bnDRc0-E^pwS&Dl! z?hP1ym}yQFXI7a(av#F2>JDUB1=?OeuF9N&e<5myygvT=)VtKPa*^BPUAbh*YJ6o| z<6h*c#0o}kOCO8;G%s`3(6E#B>nCMdl7(~AO`{{QEa_pd?m)>$(^L!7sa<9nFZV&B zyY*4U58kp!Rjovi;$vkmR*8DcQb8xEnZ@~t4}|KX)M5HDX=VY~gLBTz7+kJl;9TNB WHs1CP;c&fJkgT09=Y;9lw*Llz`vTSg literal 0 HcmV?d00001 diff --git a/misc/nstat.c b/misc/nstat.c new file mode 100644 index 0000000..f2887ec --- /dev/null +++ b/misc/nstat.c @@ -0,0 +1,620 @@ +/* + * nstat.c handy utility to read counters /proc/net/netstat and snmp + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <signal.h> +#include <math.h> + +#include <SNAPSHOT.h> + +int dump_zeros = 0; +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +double W; +char **patterns; +int npatterns; + +char info_source[128]; +int source_mismatch; + +int generic_proc_open(char *env, char *name) +{ + char store[128]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_netstat_open(void) +{ + return generic_proc_open("PROC_NET_NETSTAT", "net/netstat"); +} + +int net_snmp_open(void) +{ + return generic_proc_open("PROC_NET_SNMP", "net/snmp"); +} + +int net_snmp6_open(void) +{ + return generic_proc_open("PROC_NET_SNMP6", "net/snmp6"); +} + +struct nstat_ent +{ + struct nstat_ent *next; + char *id; + unsigned long long val; + unsigned long ival; + double rate; +}; + +struct nstat_ent *kern_db; +struct nstat_ent *hist_db; + +char *useless_numbers[] = { +"IpForwarding", "IpDefaultTTL", +"TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax", +"TcpMaxConn", "TcpCurrEstab" +}; + +int useless_number(char *id) +{ + int i; + for (i=0; i<sizeof(useless_numbers)/sizeof(*useless_numbers); i++) + if (strcmp(id, useless_numbers[i]) == 0) + return 1; + return 0; +} + +int match(char *id) +{ + int i; + + if (npatterns == 0) + return 1; + + for (i=0; i<npatterns; i++) { + if (!fnmatch(patterns[i], id, 0)) + return 1; + } + return 0; +} + +void load_good_table(FILE *fp) +{ + char buf[4096]; + struct nstat_ent *db = NULL; + struct nstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + int nr; + unsigned long long val; + double rate; + char idbuf[sizeof(buf)]; + if (buf[0] == '#') { + buf[strlen(buf)-1] = 0; + if (info_source[0] && strcmp(info_source, buf+1)) + source_mismatch = 1; + info_source[0] = 0; + strncat(info_source, buf+1, sizeof(info_source)-1); + continue; + } + /* idbuf is as big as buf, so this is safe */ + nr = sscanf(buf, "%s%llu%lg", idbuf, &val, &rate); + if (nr < 2) + abort(); + if (nr < 3) + rate = 0; + if (useless_number(idbuf)) + continue; + if ((n = malloc(sizeof(*n))) == NULL) + abort(); + n->id = strdup(idbuf); + n->ival = (unsigned long)val; + n->val = val; + n->rate = rate; + n->next = db; + db = n; + } + + while (db) { + n = db; + db = db->next; + n->next = kern_db; + kern_db = n; + } +} + + +void load_ugly_table(FILE *fp) +{ + char buf[4096]; + struct nstat_ent *db = NULL; + struct nstat_ent *n; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char idbuf[sizeof(buf)]; + int off; + char *p; + + p = strchr(buf, ':'); + if (!p) + abort(); + *p = 0; + idbuf[0] = 0; + strncat(idbuf, buf, sizeof(idbuf) - 1); + off = p - buf; + p += 2; + + while (*p) { + char *next; + if ((next = strchr(p, ' ')) != NULL) + *next++ = 0; + else if ((next = strchr(p, '\n')) != NULL) + *next++ = 0; + if (off < sizeof(idbuf)) { + idbuf[off] = 0; + strncat(idbuf, p, sizeof(idbuf) - off - 1); + } + n = malloc(sizeof(*n)); + if (!n) + abort(); + n->id = strdup(idbuf); + n->rate = 0; + n->next = db; + db = n; + p = next; + } + n = db; + if (fgets(buf, sizeof(buf), fp) == NULL) + abort(); + do { + p = strrchr(buf, ' '); + if (!p) + abort(); + *p = 0; + if (sscanf(p+1, "%lu", &n->ival) != 1) + abort(); + n->val = n->ival; + /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */ + if (strcmp(idbuf, "IcmpOutAddrMaskReps") == 0) + idbuf[5] = 0; + else + n = n->next; + } while (p > buf + off + 2); + } + + while (db) { + n = db; + db = db->next; + if (useless_number(n->id)) { + free(n->id); + free(n); + } else { + n->next = kern_db; + kern_db = n; + } + } +} + +void load_snmp(void) +{ + FILE *fp = fdopen(net_snmp_open(), "r"); + if (fp) { + load_ugly_table(fp); + fclose(fp); + } +} + +void load_snmp6(void) +{ + FILE *fp = fdopen(net_snmp6_open(), "r"); + if (fp) { + load_good_table(fp); + fclose(fp); + } +} + +void load_netstat(void) +{ + FILE *fp = fdopen(net_netstat_open(), "r"); + if (fp) { + load_ugly_table(fp); + fclose(fp); + } +} + +void dump_kern_db(FILE *fp, int to_hist) +{ + struct nstat_ent *n, *h; + h = hist_db; + 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) + continue; + if (!match(n->id)) { + struct nstat_ent *h1; + if (!to_hist) + continue; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + val = h1->val; + h = h1->next; + break; + } + } + } + fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate); + } +} + +void dump_incr_db(FILE *fp) +{ + struct nstat_ent *n, *h; + h = hist_db; + fprintf(fp, "#%s\n", info_source); + for (n=kern_db; n; n=n->next) { + int ovfl = 0; + unsigned long long val = n->val; + struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + if (val < h1->val) { + ovfl = 1; + val = h1->val; + } + val -= h1->val; + h = h1->next; + break; + } + } + if (!dump_zeros && !val && !n->rate) + continue; + if (!match(n->id)) + continue; + fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val, + n->rate, ovfl?" (overflow)":""); + } +} + +static int children; + +void sigchild(int signo) +{ +} + +void update_db(int interval) +{ + struct nstat_ent *n, *h; + + n = kern_db; + kern_db = NULL; + + load_netstat(); + load_snmp6(); + load_snmp(); + + h = kern_db; + kern_db = n; + + for (n = kern_db; n; n = n->next) { + struct nstat_ent *h1; + for (h1 = h; h1; h1 = h1->next) { + if (strcmp(h1->id, n->id) == 0) { + double sample; + unsigned long incr = h1->ival - n->ival; + n->val += incr; + n->ival = h1->ival; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + n->rate += W*(sample-n->rate); + } else if (interval >= 1000) { + if (interval >= time_constant) { + n->rate = sample; + } else { + double w = W*(double)interval/scan_interval; + n->rate += w*(sample-n->rate); + } + } + + while (h != h1) { + struct nstat_ent *tmp = h; + h = h->next; + free(tmp->id); + free(tmp); + }; + h = h1->next; + free(h1->id); + free(h1); + break; + } + } + } +} + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + load_netstat(); + load_snmp6(); + load_snmp(); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + FILE *fp = fdopen(clnt, "w"); + if (fp) { + if (tdiff > 0) + update_db(tdiff); + dump_kern_db(fp, 0); + } + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: nstat [ -h?vVzrnasd:t: ] [ PATTERN [ PATTERN ] ]\n" + ); + exit(-1); +} + + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + FILE *hist_fp = NULL; + int ch; + int fd; + + while ((ch = getopt(argc, argv, "h?vVzrnasd:t:")) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'd': + scan_interval = 1000*atoi(optarg); + break; + case 't': + if (sscanf(optarg, "%d", &time_constant) != 1 || + time_constant <= 0) { + fprintf(stderr, "nstat: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("nstat utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "nstat%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("nstat: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("nstat: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("nstat: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + patterns = argv; + npatterns = argc; + + if (getenv("NSTAT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("NSTAT_HISTORY")); + else + sprintf(hist_name, "/tmp/.nstat.u%d", getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("nstat: open history file"); + exit(-1); + } + if ((hist_fp = fdopen(fd, "r+")) == NULL) { + perror("nstat: fdopen history file"); + exit(-1); + } + if (flock(fileno(hist_fp), LOCK_EX)) { + perror("nstat: flock history file"); + exit(-1); + } + if (fstat(fileno(hist_fp), &stb) != 0) { + perror("nstat: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "nstat: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "nstat: history is aged out, resetting\n"); + ftruncate(fileno(hist_fp), 0); + } + } + + load_good_table(hist_fp); + + hist_db = kern_db; + kern_db = NULL; + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "nstat0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + FILE *sfp = fdopen(fd, "r"); + load_good_table(sfp); + if (hist_db && source_mismatch) { + fprintf(stderr, "nstat: history is stale, ignoring it.\n"); + hist_db = NULL; + } + fclose(sfp); + } else { + if (fd >= 0) + close(fd); + if (hist_db && info_source[0] && strcmp(info_source, "kernel")) { + fprintf(stderr, "nstat: history is stale, ignoring it.\n"); + hist_db = NULL; + info_source[0] = 0; + } + load_netstat(); + load_snmp6(); + load_snmp(); + if (info_source[0] == 0) + strcpy(info_source, "kernel"); + } + + if (!no_output) { + if (ignore_history || hist_db == NULL) + dump_kern_db(stdout, 0); + else + dump_incr_db(stdout); + } + if (!no_update) { + ftruncate(fileno(hist_fp), 0); + rewind(hist_fp); + dump_kern_db(hist_fp, 1); + fflush(hist_fp); + } + exit(0); +} diff --git a/misc/rtacct b/misc/rtacct new file mode 100755 index 0000000000000000000000000000000000000000..5edd2217b414c1f8d8b9525435ab5f328711b89d GIT binary patch literal 37528 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 afqHqxq@xc=R2tfeJv}#<VeyBx*#8C9UX7Ii literal 0 HcmV?d00001 diff --git a/misc/rtacct.c b/misc/rtacct.c new file mode 100644 index 0000000..5c6748b --- /dev/null +++ b/misc/rtacct.c @@ -0,0 +1,625 @@ +/* + * rtacct.c Applet to display contents of /proc/net/rt_acct. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <fnmatch.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <signal.h> +#include <math.h> + +#include "rt_names.h" + +#include <SNAPSHOT.h> + +int reset_history = 0; +int ignore_history = 0; +int no_output = 0; +int no_update = 0; +int scan_interval = 0; +int time_constant = 0; +int dump_zeros = 0; +unsigned long magic_number = 0; +double W; + +int generic_proc_open(char *env, char *name) +{ + char store[1024]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_rtacct_open(void) +{ + return generic_proc_open("PROC_NET_RTACCT", "net/rt_acct"); +} + +__u32 rmap[256/4]; + +struct rtacct_data +{ + __u32 ival[256*4]; + + unsigned long long val[256*4]; + double rate[256*4]; + __u8 signature[128]; +}; + +struct rtacct_data kern_db_static; + +struct rtacct_data *kern_db = &kern_db_static; +struct rtacct_data *hist_db; + +void nread(int fd, char *buf, int tot) +{ + int count = 0; + + while (count < tot) { + int n = read(fd, buf+count, tot-count); + if (n < 0) { + if (errno == EINTR) + continue; + exit(-1); + } + if (n == 0) + exit(-1); + count += n; + } +} + + +__u32 *read_kern_table(__u32 *tbl) +{ + static __u32 *tbl_ptr; + int fd; + + if (magic_number) { + if (tbl_ptr != NULL) + return tbl_ptr; + + fd = open("/dev/mem", O_RDONLY); + if (fd < 0) { + perror("magic open"); + exit(-1); + } + tbl_ptr = mmap(NULL, 4096, + PROT_READ, + MAP_SHARED, + fd, magic_number); + if ((unsigned long)tbl_ptr == ~0UL) { + perror("magic mmap"); + exit(-1); + } + close(fd); + return tbl_ptr; + } + + fd = net_rtacct_open(); + if (fd >= 0) { + nread(fd, (char*)tbl, 256*16); + close(fd); + } else { + memset(tbl, 0, 256*16); + } + return tbl; +} + +void format_rate(FILE *fp, double rate) +{ + char temp[64]; + + if (rate > 1024*1024) { + sprintf(temp, "%uM", (unsigned)rint(rate/(1024*1024))); + fprintf(fp, " %-10s", temp); + } else if (rate > 1024) { + sprintf(temp, "%uK", (unsigned)rint(rate/1024)); + fprintf(fp, " %-10s", temp); + } else + fprintf(fp, " %-10u", (unsigned)rate); +} + +void format_count(FILE *fp, unsigned long long val) +{ + if (val > 1024*1024*1024) + fprintf(fp, " %10lluM", val/(1024*1024)); + else if (val > 1024*1024) + fprintf(fp, " %10lluK", val/1024); + else + fprintf(fp, " %10llu", val); +} + +void dump_abs_db(FILE *fp) +{ + int realm; + char b1[16]; + + if (!no_output) { + fprintf(fp, "#%s\n", kern_db->signature); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); + + } + + for (realm=0; realm<256; realm++) { + int i; + unsigned long long *val; + double *rate; + + if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) + continue; + + val = &kern_db->val[realm*4]; + rate = &kern_db->rate[realm*4]; + + if (!dump_zeros && + !val[0] && !rate[0] && + !val[1] && !rate[1] && + !val[2] && !rate[2] && + !val[3] && !rate[3]) + continue; + + if (hist_db) { + memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); + } + + if (no_output) + continue; + + fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); + for (i = 0; i < 4; i++) + format_count(fp, val[i]); + fprintf(fp, "\n%-10s", ""); + for (i = 0; i < 4; i++) + format_rate(fp, rate[i]); + fprintf(fp, "\n"); + } +} + + +void dump_incr_db(FILE *fp) +{ + int k, realm; + char b1[16]; + + if (!no_output) { + fprintf(fp, "#%s\n", kern_db->signature); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "Realm", "BytesTo", "PktsTo", "BytesFrom", "PktsFrom"); + fprintf(fp, +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"%-10s " +"\n" + , "", "BPSTo", "PPSTo", "BPSFrom", "PPSFrom"); + } + + for (realm=0; realm<256; realm++) { + int ovfl = 0; + int i; + unsigned long long *val; + double *rate; + unsigned long long rval[4]; + + if (!(rmap[realm>>5] & (1<<(realm&0x1f)))) + continue; + + val = &kern_db->val[realm*4]; + rate = &kern_db->rate[realm*4]; + + for (k=0; k<4; k++) { + rval[k] = val[k]; + if (rval[k] < hist_db->val[realm*4+k]) + ovfl = 1; + else + rval[k] -= hist_db->val[realm*4+k]; + } + if (ovfl) { + for (k=0; k<4; k++) + rval[k] = val[k]; + } + if (hist_db) { + memcpy(&hist_db->val[realm*4], val, sizeof(*val)*4); + } + + if (no_output) + continue; + + if (!dump_zeros && + !rval[0] && !rate[0] && + !rval[1] && !rate[1] && + !rval[2] && !rate[2] && + !rval[3] && !rate[3]) + continue; + + + fprintf(fp, "%-10s", rtnl_rtrealm_n2a(realm, b1, sizeof(b1))); + for (i = 0; i < 4; i++) + format_count(fp, rval[i]); + fprintf(fp, "\n%-10s", ""); + for (i = 0; i < 4; i++) + format_rate(fp, rate[i]); + fprintf(fp, "\n"); + } +} + + +static int children; + +void sigchild(int signo) +{ +} + +/* Server side only: read kernel data, update tables, calculate rates. */ + +void update_db(int interval) +{ + int i; + __u32 *ival; + __u32 _ival[256*4]; + + ival = read_kern_table(_ival); + + for (i=0; i<256*4; i++) { + double sample; + __u32 incr = ival[i] - kern_db->ival[i]; + + if (ival[i] == 0 && incr == 0 && + kern_db->val[i] == 0 && kern_db->rate[i] == 0) + continue; + + kern_db->val[i] += incr; + kern_db->ival[i] = ival[i]; + sample = (double)(incr*1000)/interval; + if (interval >= scan_interval) { + kern_db->rate[i] += W*(sample-kern_db->rate[i]); + } else if (interval >= 1000) { + if (interval >= time_constant) { + kern_db->rate[i] = sample; + } else { + double w = W*(double)interval/scan_interval; + kern_db->rate[i] += w*(sample-kern_db->rate[i]); + } + } + } +} + +void send_db(int fd) +{ + int tot = 0; + + while (tot < sizeof(*kern_db)) { + int n = write(fd, ((char*)kern_db) + tot, sizeof(*kern_db)-tot); + if (n < 0) { + if (errno == EINTR) + continue; + return; + } + tot += n; + } +} + + + +#define T_DIFF(a,b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000) + + +void pad_kern_table(struct rtacct_data *dat, __u32 *ival) +{ + int i; + memset(dat->rate, 0, sizeof(dat->rate)); + if (dat->ival != ival) + memcpy(dat->ival, ival, sizeof(dat->ival)); + for (i=0; i<256*4; i++) + dat->val[i] = ival[i]; +} + +void server_loop(int fd) +{ + struct timeval snaptime; + struct pollfd p; + p.fd = fd; + p.events = p.revents = POLLIN; + + sprintf(kern_db->signature, "%d.%lu sampling_interval=%d time_const=%d", + getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000); + + pad_kern_table(kern_db, read_kern_table(kern_db->ival)); + + for (;;) { + int status; + int tdiff; + struct timeval now; + gettimeofday(&now, NULL); + tdiff = T_DIFF(now, snaptime); + if (tdiff >= scan_interval) { + update_db(tdiff); + snaptime = now; + tdiff = 0; + } + if (poll(&p, 1, tdiff + scan_interval) > 0 + && (p.revents&POLLIN)) { + int clnt = accept(fd, NULL, NULL); + if (clnt >= 0) { + pid_t pid; + if (children >= 5) { + close(clnt); + } else if ((pid = fork()) != 0) { + if (pid>0) + children++; + close(clnt); + } else { + if (tdiff > 0) + update_db(tdiff); + send_db(clnt); + exit(0); + } + } + } + while (children && waitpid(-1, &status, WNOHANG) > 0) + children--; + } +} + +int verify_forging(int fd) +{ + struct ucred cred; + int olen = sizeof(cred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void*)&cred, &olen) || + olen < sizeof(cred)) + return -1; + if (cred.uid == getuid() || cred.uid == 0) + return 0; + return -1; +} + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: rtacct [ -h?vVzrnasd:t: ] [ ListOfRealms ]\n" + ); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + char hist_name[128]; + struct sockaddr_un sun; + int ch; + int fd; + + while ((ch = getopt(argc, argv, "h?vVzrM:nasd:t:")) != EOF) { + switch(ch) { + case 'z': + dump_zeros = 1; + break; + case 'r': + reset_history = 1; + break; + case 'a': + ignore_history = 1; + break; + case 's': + no_update = 1; + break; + case 'n': + no_output = 1; + break; + case 'd': + scan_interval = 1000*atoi(optarg); + break; + case 't': + if (sscanf(optarg, "%d", &time_constant) != 1 || + time_constant <= 0) { + fprintf(stderr, "rtacct: invalid time constant divisor\n"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("rtacct utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'M': + /* Some secret undocumented option, nobody + * is expected to ask about its sense. See? + */ + sscanf(optarg, "%lx", &magic_number); + break; + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc) { + while (argc > 0) { + __u32 realm; + if (rtnl_rtrealm_a2n(&realm, argv[0])) { + fprintf(stderr, "Warning: realm \"%s\" does not exist.\n", argv[0]); + exit(-1); + } + rmap[realm>>5] |= (1<<(realm&0x1f)); + argc--; argv++; + } + } else { + memset(rmap, ~0, sizeof(rmap)); + /* Always suppress zeros. */ + dump_zeros = 0; + } + + sun.sun_family = AF_UNIX; + sun.sun_path[0] = 0; + sprintf(sun.sun_path+1, "rtacct%d", getuid()); + + if (scan_interval > 0) { + if (time_constant == 0) + time_constant = 60; + time_constant *= 1000; + W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant); + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("rtacct: socket"); + exit(-1); + } + if (bind(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) < 0) { + perror("rtacct: bind"); + exit(-1); + } + if (listen(fd, 5) < 0) { + perror("rtacct: listen"); + exit(-1); + } + if (fork()) + exit(0); + chdir("/"); + close(0); close(1); close(2); setsid(); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, sigchild); + server_loop(fd); + exit(0); + } + + if (getenv("RTACCT_HISTORY")) + snprintf(hist_name, sizeof(hist_name), getenv("RTACCT_HISTORY")); + else + sprintf(hist_name, "/tmp/.rtacct.u%d", getuid()); + + if (reset_history) + unlink(hist_name); + + if (!ignore_history || !no_update) { + struct stat stb; + + fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600); + if (fd < 0) { + perror("rtacct: open history file"); + exit(-1); + } + if (flock(fd, LOCK_EX)) { + perror("rtacct: flock history file"); + exit(-1); + } + if (fstat(fd, &stb) != 0) { + perror("rtacct: fstat history file"); + exit(-1); + } + if (stb.st_nlink != 1 || stb.st_uid != getuid()) { + fprintf(stderr, "rtacct: something is so wrong with history file, that I prefer not to proceed.\n"); + exit(-1); + } + if (stb.st_size != sizeof(*hist_db)) + write(fd, kern_db, sizeof(*hist_db)); + + hist_db = mmap(NULL, sizeof(*hist_db), + PROT_READ|PROT_WRITE, + no_update ? MAP_PRIVATE : MAP_SHARED, + fd, 0); + + if ((unsigned long)hist_db == ~0UL) { + perror("mmap"); + exit(-1); + } + + if (!ignore_history) { + FILE *tfp; + long uptime; + if ((tfp = fopen("/proc/uptime", "r")) != NULL) { + if (fscanf(tfp, "%ld", &uptime) != 1) + uptime = -1; + fclose(tfp); + } + + if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) { + fprintf(stderr, "rtacct: history is aged out, resetting\n"); + memset(hist_db, 0, sizeof(*hist_db)); + } + } + + close(fd); + } + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 && + (connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0 + || (strcpy(sun.sun_path+1, "rtacct0"), + connect(fd, (struct sockaddr*)&sun, 2+1+strlen(sun.sun_path+1)) == 0)) + && verify_forging(fd) == 0) { + nread(fd, (char*)kern_db, sizeof(*kern_db)); + if (hist_db && hist_db->signature[0] && + strcmp(kern_db->signature, hist_db->signature)) { + fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); + hist_db = NULL; + } + close(fd); + } else { + if (fd >= 0) + close(fd); + + if (hist_db && hist_db->signature[0] && + strcmp(hist_db->signature, "kernel")) { + fprintf(stderr, "rtacct: history is stale, ignoring it.\n"); + hist_db = NULL; + } + + pad_kern_table(kern_db, read_kern_table(kern_db->ival)); + strcpy(kern_db->signature, "kernel"); + } + + if (ignore_history || hist_db == NULL) + dump_abs_db(stdout); + else + dump_incr_db(stdout); + + exit(0); +} diff --git a/misc/ss b/misc/ss new file mode 100755 index 0000000000000000000000000000000000000000..86af12edde2e1242872587e6cefe633898900634 GIT binary patch literal 75189 zcmd443w%>m);6BB2~=!7K|!mcR*eo-kk$c*DriGfJk_9e0PhM|sGtZ8O}GpQ#U$D@ zhiFG}hVjbyI*N{?co`_wNz#^HVAQF2Ll~tZ$~i=|0}2Hy`JQK=bJ9?B=Ka6#|NH&E zKzhzvd+)W^UVH7e*S=<Hsc&qC-EOl!8Me!9gj}Z=3zYE}uX_Y&;zruCY{j-SY^T}! z0OQ2BScLFtyjjn<N!og5nej65bnrdHL}U~jFXnTQ0okml#UY=}ujf;2z8cSGI}Ffz zq9g*rbmVn)hXC;`7cbVc)?}>h7e=_1j_fgAdxwE<H`7_qRVHKB(<&d!#_u4re1kHI z0TC|d!<Hxh_B@3wfGhvfKb7y=CL@BjsaU*N&kYnDKGxGpcOBA^&;Rbn;&a%{8FySV zeAvvXLubyI6<9EI!N^O7UNZcms@WG^EcmDNSi~!@nJ7rJI98rSl;C>}zQ^HvHNMB= zdj-Cu@jVmYG5B7B?`8OU@a3lrUyDW&-p?b_{FGWRc%F~%#rPJOxMDor<~#W~1>aHl z_A_yOke_k*lIL7}v+*@Fz-AkUcYcN%4_m6L0m~+ikF)Wme6AvZPk(&JiZ??SMw;(l zJbmW-0P|UBKL7uEocP})bXu=?CjdSR-*bB5e+*=M%EOm!Z7{wk<I9hP?@8j#X3N3z zB7D!lmnt|A-@f<`!Iz((<9oVzv)S^^Cm$E$djY<~@jVyc5%~TL-^=mk=TdyH#P?JJ z|K{@wDl}ad_nC16P=s{+J`?{*ulOMoKcW|&yzuj7FZ_N3ZF{sAeJkG&5T2gzH)i~w z^`dhPV18c2H$DE>0&BB<(F^~eiBI&RbCyZx&0g_8HREUZithm5{HXY*=X;Xb@XqcP z|8Wu9W_!LDex-?jxEKCp!2Dd>OFmCgIq<ol7rw}he|s-Fe=_msB7T3g*kawV%a(n5 zx>tOw9&>upx8(BvoO7aeaeI(Hjv+8*ddM>p|*?uEDXWJ@nP=K<!YtrvcRfws|} zrOW@HCcdgye5>9M^oqaHq<<QoELVB2a&`8KUun|+V=ug7;&=DL-)++G)2qDanDGxH zJe_~*eOfR4J0_j#UU;^Bes1W6f8Rjcp6ZpaRlo1`if_rMp%?x%r03^IFMI`He*C@g zs|40&YwCr!@;!oZtBj0l)q7(vyrn-X=&)XHH0#A|UbY8&mFpRkpU-;byO){ZQ{M~! zCj)I`{iO55ewCk-df_emc~!4`n~mtUn|k5dp81)AZ#w-41lDF-)Qi3)htqq}vFc@1 zulN}zefE#(^esBGdeM2_%y&{RI&(~XVXye?tN0ny3r{}y>D!CWRVMzqUh$7J@iUPo zoli>+(|g75C#W0#O?vz_CjOVb=!9KHz|DA2PbT+jzw>*=xAcn1^6>5bc@&8Bd@Vca z20pKMWWw<8=1!}cJ#(Jn(@nZ+-lShnyK_dBf7;xVnNzB&rd8P{O}g`**|R29`KQeF zPnyIO6^wj|ZBD>nF@4HhTa|xq#hit<8M7<=Gi_B1t14#CsswWC?10}^HO)VLHmJ=K zuT|6L&g1JOP@C(Y9hf<D(u`TiYSxsQwi&ag`6tcs&z@zwbDDql9RH-5vuE9DtC}@u zE}~QtIcM%{|Li*!&I;Uf$F#XbOq(^&B9%rcE#{Parr9d1DyD#+N}#K3_e{Hotj?J> zckb-Dwz<=$Ood;2H%o%BDKlrzuCUFhn&S5_w1I;ubMLgxnLTHk;E1KTW8svkQ&Ha8 z6?aebi_}wR%(XZOq_I$0IWtf--BwvKb9U7<+mt(I&qeY5bLUn}pKGf$3v|bfSyL$< zQbT6UkQj1^SP<WgSqPpr^`0t7!9RBv`KF|&R86b6XO0N+&$d-g6$}c2P7MHUmX<{$ zEo5M;^iP{Plk%eY?^rm8-1uieG)&48kSs{1V$7o4s!Tq?38ZDKoIiJle;Nu4VAgC@ zPQ?`e3{+O-tb3+F4Q$gE%<vm^I19B$T86|(>tb7_;1c;%&6x!n)FD%$?wP`J`sdDs zn8^pqE~G}j=1!}a$KqGcoi<Hy2T=*$uJo0SDVcQfMHgR0hY^Fs|M&hGLsz@aE_`3J zncD)V%{Y!B>tHwdnX{u@JUKSwxWxKcehz(a`qI|N@>_`K_{I8I{XX$$TSz>ueuwyr z)9{v^A-*gPUu*Ky7dX@OiO`j4c$0O3UzLV8T|0qaorbsiJO->u!!vK|Q<sLPEUZsF z4Nsn|Pg@$^vT+P+Ps2mdsn3=)yk(;q*pY@mJ{23|>ok0J8oo0P&oPhn=}N<&U?KVZ z!l+;J&he7<$x6dpy1_748lK}y>yw*?Pq*)RX?SZa#x#S|@YdLq_<}UNHI5^GWE!61 zX6sX&hPQkOhRJF8^s!}m8r~Z3G5(Edc&l$FesUU~V@K;VJq^!!0_!s;4bSnb^;wXH z&$EztR;S_5NyFEq;W_TKKDB9h_L<gaWg7lG3yJ5dH2nE#_|<9npQYi~q~ST1wmx-f z_<ReAXFLsW`N|A#OT%B77Qa0WKO_ymB@J)QZ!owc4S!Kud@T)c&5JO&GYwym7QZVE zf7Beu&K~{e9Dw!7O2c#PZ+%>8cwySjfZQ}Z=ZmaQUK&1qzGiS5-kOhLnu0Vu=a#I` z$TU3X9IQ`q8vZg1iKm=~AC-nLPs3ZjID>CY!&`G3#7|Dcb6(8)Oi#lXTSz?Tq~Sej z_yuYBF=_bfG<-=Kz9tRtO~cow;Y-u-E7R~})9|a(@K>hcSEu3SH2j)0{8efAx->k; z1lC6mex0Sgjvhoi5#5FM>BA6xa6?v8a>S0myKKqxcH(a>KFRY4PiHc{BZ<#>TM4rZ z)7u5SnJ~L9Jucu)gxO{3YXsa#m|c~=O2GAm*+uEK0)CA!yC%I_z%LS}S=Z+X_!+|N ziuB0>evB}?AiZ3`4-;nBqZbSKH-y>c=mi44k1)F$Jx{>DCd@8IcL{hdVRkLLO~7{% zW|yLO9s%OKI|#EY(K`fuGhucidb@ysNtj)S9vARcgxO{2YXm%oFuMwUm4GiLOxHuN z74XG`*=6X}0=|GSy9#}dfX^XJmqec|;L`}R%h1aOd?I0X6?(CN`w?arp%(}^gD|@W zJx{=gHUVarpt}UTmoU2m-6r7OgxLk?oj<VrI|%0y?hx=+!gPi7b^&iD%q~EW3wRS@ zntpwafEx+Z<m;;hTu+#$UauAKYlLay^=bjXNSLNwpCjOB2-BqNlLh=3VVZKiT)+<# zrU}=J1^gSrG~IfEfbSzrldb0o_}7GKs&$uu=Mtug)@=g5i!e>I-q|hcpD<0b-XY+d z3DXqo?E?NKVY+~NT)<Zmrb*V<2zU%(nqqyGfG;IX6Rg(?_+r8|y?V8PFCa{ltIrYe zIfQ9y^~nN0jWA8DUM}Dh3DdOd#RBd}m?l*(5O4-zno>Paz=t*hrU}(u0^UoQrc<{G zcsF61Ouh54sDHx6ggXShl`u`D-Y($HglQV}xPUhirb*P-2)L0jO`*O@!1aV_0`*$J zc#4l)xTj2c?{-<)AqRJMmQN^ajTgL7Y?E6X+E??@8m}z2X-kmeyIR^VS#{>Ox@>Yd z?$1cJ3)4k&mHJeVw!e!;J5g4?^t%#nq={$AigO||Pi{tHA6{hDC5JZ$cD6V_e8*+8 zZ)~cptgJP@wG-vw=(CzZfdo#jBah50JJyDUs4}H#qbK>r#->zuwX$-nyHi$Xy1V3M zU$10r<$GEALYoPWnb0jO+Y9#>#<g3ZWU~5%yM}Spm)x~{F5AE)O|d)wT#STYXqO<> z`d<+U$IBE=mP*^|=L3u`6_j^@&UjQ-;zhwQc&?4!i{MxofAn9W)@q{<6MmfV6al|L z_*KH!2{=l)m9R&^9}@nQaG`*A6W&MoYysO}0^ARO^!@_QC44SnyMTXA_zJ@Rf*Gle zmJ_~-@aF=)ldzxgdjh_n@Cw2W0)C$G8p5v%I7)aE;Xewvtq$-u!oMZ#xuG!bxxP%< z@0nPpeD9esaXjSShHfI9R3^CdTD|UEh{_f?5wa!vmfJkHdT#UF?wQo29Fik%xvPPU zeg}G*0!hk|<pQJH<zRy&Qky(-<j8^cvm2M~sK&>yDea<ee356})#%F_G}nQ~>S_!< zyzO$tlOapqHrr-75)wq@;3t{+4T{e_NW14?G8wh~MO0)x*IZGRojzrkwgLgw-~z;K zEl9==(0{d0`P|gMpJwDI@<yJ%{Lic3%3cbR!~7>QlUI;QWoyX6T!z^|{HLiR`cD(~ z=6{aT4A7Pyf~1t@!nhpXBZZzpJ*ZxHUb0PIDJxz#+Sj+5&(L=%Gz%_*99eLZ9Pzn_ z$&m@}!G-Z?Ur0hb6;zUKf%oJJuX})sfM_<*Wsy-1sC732!D#LOic^>NAxe(6BP&f( zXd1GEi04q$XocDfEJZjjh3Lf`#iH9{aUPOeEV{s=J6h4h;!R8+dBa@*g7q|%R()RP zbq@+WDOa>vp_ZO?%S9XaJr}sor)a+XP8spCq{wchga!z<{CCS0es><iy{gac_Cl<K z853H`aG&yR{Yg}!{1%AlrY?%;TkX9AU=n&>W^dNrc%}HC+@DO2EBfB=^t6;FZ6K3- zto{p4*4{@<kq^ZbS?n$l_NM-9WD~U^Pud)0_BApQ!O`YdP<KBVaxGI@rBE63jI0nG z%1Uc=2$CYj%^>VklG<K8_45#iHVXD}yJjQiW7t1n*nZd3YwY%1@0n<}-&|IRve=!q zPyTZ&T|k@m2J}f}t+Z;L;H6AysRPaAxbS}eOkZTC+vQXCMmeS8i8+ygf{_u=!V(_I zGQb93T)4+q@#R(Opf8KRDi^BoAUNull?J&e?o+-2kNckU4^B1*e{jtEur!=py56T8 z(DD%pD!*(4*-Nt{GQ^t|{p*@yX6i})<EqGjRVe?Z$PH<whZcg(MAU50;iTw8JQF0j zE6U!u#aV~`%O+QRtQ?#u&)EF_xI0`82hZ{--Q(qk-FXd%`Y0dfZ_0m1u4tBL#Fd(w zni-olxYcrlmKV9zX;U;${s#?RXEhvl<~J!@q*q$<o5S0r@Y|pr-V>Nq8s6z27mMrV zHX(ntuN-XsQNtD;iF!M_?O#cQ)@9r5WpBJa6NQ?FLb2#|B#3^pHuyt7Q6Q0nR~pyx z(;2M4f;#2-$z1BjB8d5bWo0)_DKh+yx-t?HEQ~9k)L%sk1t^)wuC#EcHsSkZGI;nn z=|MX2M{&mDR@SNmGro7(#w&-zoBgNEXrknl_il-JUCBQjIH<JhGhw{68x{iFs5n3V z&Sm?NI5Of`E`jHIp@p$dbVDh9t%X8p_4|Z&!7fUnPpE<DBU}jZMf_0Ch}pv{jnQfb z!YaS;9r`a>*_}eH1VR|3#}H#&<Qj)B;&o@!W@Z&aFZF0SiqDkW5i!{12sFm>5L3&u za&Gdkfi7l4S<&yg=FX|ONGZwEK4-8x;=TrWI!X!J$+z0&AP|lRO5p(AZJ=}Ux^f?5 zr1+7QGwv6dOznt~gA}fWl+glMV5<c*f%cgoLb_ie9_StiV#foOcusCD5&dssmE}*N zDlET^#L*mw$m+mHHz4-$s4rO+xZI)}vjY3%qVn9p9$77R(XqSbwkFi4bMBWe8&X|W z)U-J3vNNUwS~5Z%{RRkxlm3O$I`T6XkkZ<qWi{-|I<8e(m(>3GFCklhKW1PM<D)PR zs@n6=YRkAhVhOX^UkYos{-u7`+3Hv0PjoV*4~@K!lu*{w_M+Q!?%e4zJgpc+V$(i^ zU|XH%B71`REQqU)R<zZ*4iSyFSMl}$28Q~j6ee6njZR~S@`F#A>V{K5-!b_0;BYc< zaf|bClgoBNi}QOtY;}3NVXo9$(Zi}Vc<NMlt`Bc{D9K<pM*R1x&R3%@TPY+o0dgIU zbn;~b=fzw$DfBK_l9hH?j*P-Cue4S}KO;AEIMF-G%bHe!c%qN2o-T*C`3s8f7V;l2 z<X-~+I35VI3ZWi{(E-HB3WPBc($c4*91zIaXeNkPrfiTxzd=@pat1pE7J2wlGtQS9 zUAA#(-*;z0HBcsURFdm|oN7H1lJzOOsibnl4yUZ%N1kLQQ&xdd9up8c>~I%y(&tio zDPh!CgH(@1qU4Wa(YGyPYctBJ)~z8rveKZGltOobh~{D;IH^GWLsg(YX0jrt3MW1n z)hDg(>ruYL5I{ZS>Q0w!RE88%$a>@k2ZYV=E{~{ZS@~RBxPXOfg(1)`LE+UAxkysf zx#ar%&PaKmU_4Wj8-pF)kwFElFO<6rU7?zD31XEgaWp{Xvsk5}Yv)tfFh~#`F$M`) zbOKb^6?V+%i127q_9g1Q>a~v2@Ww#@7Ki(S7O&fmrU&9I4_HjG7DaqpjJ>V)!F)2= ztS=VDW>n+bQ+`=FY=|KE^+=EM0md;}tjcC9+ziz^zWQEUhW~UfnhS-@qe2c=+F1bw z5c|lc>dX2CI=t#FU~608W3M_ss}w`5cb6@~3`{Z^XqMGX_A1IQnf<N9jVgxUh>jFh zjR(ID?ONogG$x+3m}7awnD1JLtS)iXiR{tp&y>R(>-N4}3}*$M#%5W0R~rP$K~+}7 zrL}Q3baPCXR=;WG%SET9d>`yOPI};TNJoh$-OU|;Ek?EW8D*D37Xde_KO9};H_9c2 z?g6YicXZGWL4^oS99+1usB!TLmkoU6EtgF&CSB8}wCUUzBde2Ka%9PX>dWl1@|hI+ zGjQto0l`B_|M7+K%O(ziMpKE_m1M}l<^h#daqjOBWb{vkj3>E}8krrP^!mY4_-`WV z_zV%dGQ1h|fk~WV*1vj7E*i8HdKoFDb>lOn807OA4OoA)A~fqyMZ&~FEB;a!`_&Ib zpcLXhBhOp-WAiE<vN{MAHPfBNdZtazMUyt-KP%$<x60}w)Z;hW3IPk!Ua7PLi4zTs zt1U7JuZSD@v3h=t>Xk0FctFwEA<~oa#5P$?fltK~Z>PY)4E<Xgh|rhS+Xq8Ow*|80 z;E`^@00w7DdtxOsME_#S<0&Dw63C<7l!qP7FeMLN^mIZ{$MUDo6N7i7q%VqOQurej z48_zx##2^rcNFfSA-$;jvSNQ<Mbl5Fxq|b1?$>w*yZQtYa%7P`ItLA1yW$&xa|S;C z8zqg{8i*89MEYlVQ^yW&(9Y7|$E$W2CdKNjc9E7C-T2RDUp4)!a$Ef>_EpOa`|;6k zBhy(4_EiO+p|83Q>}t0op;_NBbz9izAqCL^b+7|M&)}y8Tr9ex>FOym;H=;wG?24l zLzH$jkb<?vw#wQVO^WuE$OjdS28^~SD_K&g3$9kHy^kZyR)--r8o{mZkyM^yDv!*5 ztK6sh-8cHw#qP<_rRk!H1`pYN>U>w}smap9J)Q#}dxEW5@bSkf-}#4mq_rn_g3a00 z2m5&<6J4H2sl%guScY*~E_6&IjCq_g<pV;256dck(41t95`t59C%tC)_+-)`DJz4e z&|kn@8JsOy$#!ErQhj;9I+m%Pc3qAH9CGB|Y^k)}ThkS}Ia{vS9J$(2^G~^A8-_HP z0QlT>T$@sA18aS1z>Ye0fNcL%8SC&!>&9l(jJ3<^aJTj}xF|!mXo<qz@wu~<-D~V` zI0TK#3;TN&y)yqjWmCfr*KzG2XG9J*qdcNN4Rh>jNLS5LwEQ;xFVF>=MYbs!9%i0) zF)*@vZx*3krAa#nuoQjYgYGyQ_eN%BI4F?sv>t(656dN^3i4zrx=r+0TV;Df`<G5a zAOZz-lu6MKwCw`37@2$tMUqirx!N|oReBK0as(S4U|p03{G-379w>vo&}4^T%V4rV zi-?9}FgaLh@=DQ!wh%bHba<4OlE~uoFceB^<I>pV)>HCR(8H!S&g5~h92s>Xc>GAa z%;d2^^O!si)_%b^MfWP-l!o^tP7?Bx)_UyH+IHFAkpEG5zx3c`pf0Ujm=PK4kk)-% z)EM}5OvLfCO2w0rct1_PE^RZWhcJymSx$y1yR{c~^@#F#lSfw?k1wzckd;e&1@+(R zxX_NaAY)vgMN_Ke>pCZ#&^N@`KD3)j&QwpU4ZVSYRwT=&=^jwP&dg8|k3)YBsagM4 z`qPd{?_bbrcOq?~u_xAdU-iz#|I1c;24g*8=#xcn)3GrJx7sriQ=gr38fwG)7n~@{ zR1)!`>QJhaz?b#|n4y6|e+&B}Op35CmQ^V&L|I{U=wWoAPamm{bz%O_r&i{AgqITR z%JBa@*wxRo*5$ttqwbWW*W=^SdJLxZFgy;!o|U1!$*9-Ra)=H!`8^&sIunKuQCk_j zHv?=Y{2s$S-T5WtVVKa{WVIAQ(B4ugFWeEe)FpgTwKNw`jF3um1$n+Jw;9c&;-fNn z7oKFRRu6f3YpU$feMhBIlHoz}noN_Qzyn^TOTQ!~H_PFw9bdQ~#V>#(r2XnT+NWLG zAFef?_HvFJs@54}30TO2TbdwGVX7fiOkz-YrflJ>S+WJa>0b4`OOQ}ZjI5_Q>&Ea* z##}+L+s;W9ubVSPaDeH{c<Sy)ckwF50iXGl)_u=O;pdpb0O3u#?CUwDB1gtM5%~KC z>EZau2Dn;V*`Ea|Kzh4Pn#|XRj!g16pb|nq(U5WVPE*E>{&Q&>Ak_>$A;mF}GNumk z;^NC-ya4-NPiG-3ZU45vK!M<ytwr<m)IEe!7@R7fp}e9bZyjBd{*@>?7U|Lo6ulHt zdY5XCr54Tpb6OC*w%8tu`%j^_f@vY`f`CI>TWY7HmV^MMwV5G!f+tX-YvX2c34=q2 zX#Oy*b^@d+^3JnqZL{D;Z)D;DOtH@EK0#^ZV0A~<agEZt&6v0zAZEj%(kLH1&WA)T zEob>eU)oc?)}zbkvdSl-MaF0Cd+tZem(x=|DOElx9HlWd3gcn-(W@{+fh&vL>!6FB zD$P}sFwumPauu;V_GnriP?KbE4v}-+)28o%r8ksKe+sW`l18oRH;KfL9i2F&fSvBx zDq=Y=j!ZFo+D{HP_3^yb4+yY2>J};VCZdhHK#tsP_q(IRM8xgdg;YA|WjHPb0ChdA zT`6$Qn6>s_jQX>oN-rqfIO<X<tb(1EQe=^AHUgwa8%JFrJsO7y!i`dxF1v7}FF-_7 zLTBL>aYCoyDaJBrJ>P0?-eHJ?-lSI@5&yttE8Jt&N+}H!cg|ymD~bn7wA~|>ey4r* zg=iuE99d~0krqrXE6s={g$^KR%(91HWoMymjpnW|Mkg}b@jB?nwexnsPu!@#&Axy( zrS@124P9u|+5pztH`?j98vG5U#Mg6jHTFCNj3Bn{Mzrn1jU!Rwg&;{Yfe1ETLt!9x zr{^tlt%s1-Q%7|aYqd}blX&zNgLndoRp(o;86%{fNW%d~!BvLd!!o#~hguOwbQW^5 zDSDfTvRw;^@(6<rkL`OdA|#ukpgQN^AorkF8OV{=me_;Ze73=o3^t<(Qd7y`;Mw#V zB<kU4h{@3vNUu7d!kDeJa6fvV)frNF1ER0COJUB|!68JI7skh7#`aNEx&H(Y1|6QF zu`Vgh^#O*5*x2aUxq3HOx4s<`(uSeh^b^shyG1jNy(|O^rA-%X9{3}(w95K6q%dZV z5}$MCD6-j{Iob+(QR+BtYz$$Li4@w$dN5SvzcBlc3IETDQAa%a7ncz6pSFvsan<?8 zcKG^dffWSSn1MASuvP4LKsm=LN2KsjMDZa#qN9lZJACTs>kvKBU%t#)ju(IFIQ0^S z$Ytu-TwhUF;H&-4>k&J{|7n@J1QSC^|EJWe1c+nw^d479!kd?P;R<KLn;OQ-PQ1C_ zc@EOp{jVi9dC_BI6?AvB9GWa9$p4F)Fc<}a{xV=K6)P7oX{2V(4gKaS0UQO<rny?6 zypQ!h?#R|I?GQq$M}g3<eNAUSGCjj+{7}Brj|zSV!M@1kJ}t#gn~1{sr!Eu$Vm2wf zD5A^}QQ+Q>wT~*69{LNAZ~-H7JGyt+!S}nMK%u3;1`lV{JmFq}4Iy|`;~|9xqKf5+ z0Y%>zy6~+WDS^*=HN5oknBKh_6T>Chvf{vslXe`lkCY5S@E`=|BG`@KJTs}o4IU9~ z$7f<0>rrIk!gAI!KFid5MNOOt6RDm0Eo=g2X{69Dv{q1@1BxR-u|STz<gUiJ29!sE z@^DVL+=vJ>qYF?Mm^S7Lq!{<WYn#l_a)eIji;S14d@06@2m-~D8;Jqgk_mi4!AfMl z<l)7~7x+je<$Q7B#mg5IyrkGHa4t$Btz9m%EZiLIa!J>|7wkH|W`)2=50-;{b;S9f z!iwyd!aOOYj%fd<0j(so!d-{bt8cj9z$3N`5{7*Ceu|d<hI=(oY&#cXs0;8Vf{{f7 zw9^SY2fj?x<evf(Zy^MW1J|o>x#M^Z9&*;v+mU>YN=O81b@Z$&jF{?j_Xfm@?L}r7 zt4gKI-EWh$I{ZT$M&Ggm8=}ny3#PxRIv+zTVrA?FQKar*b{qYeo1MX;V7exbqC_0O zENbxoe6MstLwNIEslV5~Knn5f8!(MA2d1_vr4xPb1!6v9uXKCcUdh#<^<_~*?wuqV z_ZJ~<Tpf6#AS-ChkwS|t8rc?&IY-lI`lpc3*PszRl7Wp40}v5q__KlU6!;|YXBhZL zh+pB>fLGcvD-m&IC@t^7lDr5j8Qfv_=fo~BV$MKJ%-28jk|=-pmk7rigMj>AR7pG8 zM#VnKfH&N20Aqzl(u<H(25t**h4EMsu-a+BaiyI4DTOB>QH=IcyAMJ@*S2f}t$@;= zT9z>TLpIip`Ze7~2U)9*_-L!k)*2G^faH8-*&!yrvbZ!Lcx4Is!7ByK5BmU)Rtw|x z%eXD#E+~O^-!=$j#7IzO@N@*r6(RQ;BoX1VI-t4wOEFn~BVy`TGJ<6k#-L#688N<z zT#eQ<-k~`V2LOz~_}nOijYhv`YpT&Z%tr6f`=C&Q6j+1yQh@a~h=)dCC)yUY?!n)n z-vb<z4Z(g0I{G}|ViOF+1|ZDp)Grrtzlq&u1iiqxD;f7r1F@2bUjm_>e>+5t1+(Wq zK`(O?N~8?{#?)>GL>TZh1{_A$PRt@=<^p3h4B@Pxiaa;S6^*6JKg>NGrb$;CIVQyP z6HJs3gFM<4^qGTL3BrnK%RgrMlt!uMd}u<^7Gt7s9OC#Yc8iV^<_wDoNI6KmVhe;| z&H{Z65?E6A6=_nW1MgUB&+kmeeH9%XfMi`A88X~w?-Vx6r*v@f9oD%l;uvBiS~^Bn z9d4N`uT(J17cCx=>?egE1u0L9cSsWRq=$@273JF`HZ`CUYJSf=?O*upU;2s?OWsC4 zgV0sp;>8@83w<Xz_2lm<t3W@x^{U8_pKG(pFGjpBbd`|6H*$UgLE6fXj505{1RE`i zw%nT?-b|sO{5HSrweR*}09~<LzZUv?b;LWuCKqY`v$PD<TB|ryj3MO6I%>DoMz9Hn zI4**vcExa}WTQ1$uEhSQ$n|m*{Tt-|rDwu;)XN-I!kk!pP`^>Ts2q4Tu!SS;@M<t3 ztK&K_c_D>(zD-(Zmm@bhFkjN`33lHp-M;}yHRlhj(Ns42&x7~or291kZIA9)9iLhL z{;}DHy5&O(@MjUpA%zzqP`x?}r0JzCI~Z>nYBL<QGnF=BAot2Jf0k`?ezydsVHcD{ z9q)y8zep{SVL=Ce3Utg5`i$MCv>S=o_+eBx;oGRa#zZCAFsNrL@fgkK(Ucz}<p?8H zl^<m>7Ydwj*^wid7?kcOD{6^XB*xT%1_gvsF0JtoSIg1$Pj<lehwnoq52u7u1w-{L zH42ukw28zb6__ursgy2qo`me@9j}geG5O12TrIiP<aOyWynb^pc>Nq|qgG80?+;uo zN2Yb&;<<HWlZ@#ub)F-pB8e(zfg`V4oDH-81|sX1ppBa<5PJqOfPVt`p47n&)rI@( zr&B8;x4I=c_ywBDcCD3>)IdjB#RmZ8V2eY0_CwIi>Y=BWl(RFu62(;FzR0v%7SXDR zz0yr>D5&#Fu#NYeUx?@J<5=)E>4BG!AA@s&7MS~qVQ&~+11{Mm!deOy(B1+oKIN;} zT7&&h$;M{dvPJ{(HW1V;{XUqaSc`#N$5{6;mf6`oN5n6Hkk(f9iM=CYDSLxoN%7qn zd~DY4XZSQy*k(`&SY&?=q~Ppx1duba{f;lu#lYy}ApnZlGoeiB#_q)L$IB6{5-t0h z{Tmpw!}%x>uh+V2mb(Yw6%Sf>EE74>#~&qqIAyTHJs4p<`09fh26_mSgg%6xq2Cw? zfKH>GPgz>KLcDQ%k^>kyQkA9cdmnX}9+GQ><Z7QVgd4uPKnyd@@abL>9AOL~g?XS? z9kK9z(G7+l7tpyTG?UQyQEJsl%Sjve0d(>W!81pO_hZ-xki5dQ4AABQ_NoJCKg*SD z==twZ&t5g>1_5uxT8n_4R|`0f7FAVD3slW~s=SsLp@CtwY6u*NT@bn8U#%)&#IcMx z+JWR)W$&ks*#BQ-+kZ-|)2POmSyax3?a~e(LhTAi<5biw+zeKbVKKSnj;hMw+|TW( z&ryzfj+T-Pr@CmkHh><Cv=-}aYp>1}-r~_Nq7ONsL}6r%g<GsKn);wF@|u14S6Utn z3j1)5C}rrf*@yQ>QXNJ{=#N>cbP8Ch4*h%t&;g<H^maf#-i(x0h|uG{plLS{d#y>2 zL%A)GKJpE7x~ErxYppsLo5oT8Si+FlViI`)2`p#fZX#xzRD1dp&kgk)_!^tN%9QU! z*Md!(%HbFo#-jGuBhSVI{TgSmu<z?1<i(@3)<XzMsV0D~sGAC#hiKS3hKi(z)}DtR z)_DCVUislPXUM%J&t@y$VYgxXqe$WsZyx}AqImlVZ}v=MlNrf&0DiS^YTp^eN=$}Y zg7&_`{RQ6Dp>O}u1#LW|0S_{IBQxg9biSb74ghP?+K`AH++;_SDQ#35gPZzbEgY{k zMhI3)(Si)t>28$s&t%b-4x&S15X%U<x!KGhn6%5MvcoS1BxZIe3ok`Rj<tD=u;U0_ zm|Qv7m?=lzFp!u_#H8v9_iDuODsYrJUz|s73rL2ES28gY785&?SfBEedlj(|+8R9h z;d@=4conJQc-PKH?W9J`HJ{kY7cv&nAs8Ft2*L1$6V&DWGr4!stTI_l7h=O16kbde zn`L9UQk)M)K7y`Mgk!iqx=bNab|cAuD9(JLY*^%3{xNcpLL9PSvx@)JNO>kUqjGkX zqvYU@Zma`p$Gyv~XA-=wB{yRf#!?5nq<PmAZJB@7vU{_rb%C>`b)}BVU{|IT`WE4M z!`6{zyn(C~T7eg=Ibcf&)T5)DbnVUg?}O-)EaJPQ`LFE{x#M{@TVCL2T*Yr-lT=6d zc^dnhwnq28Ec)g=Fk(z1-Ou_^b9O!DvejcWWD^|6`n>|{cgs6ykM|+D_Af*Vj>Lk9 zm@f>?Vxv8SQ8Y%G1Q2TyFv?S~#}j5ixfu-)4*9YTY?(a+EI5zBdu``R_rH#ev?oAR zo#=|42SNw7DlYAD5EH|)V^V>^v1Cw6F*t&`*T2mKiFdeunWas|lvku2+dSYKv88YZ zwU1rY`T`n|N+>p#X88vr1hk#y?rNZApy$XH?U`1)nt%xosDBN@f^i2lgO);nnj<GQ zC-x-Ra3RQ$Qz_Z_IoQzsD6%0R(Lcxh2oXEWZAh)H`n$OVFV;Q2)H)%5F}!GsU5|+V zB{H@urpQv1*MMj^zF-mNI0Cn*12BF-@2PwuvXz6obI^9XwR|Je9cs24{(Ug+QZ~s& z?Q<`L?h6(2Po(<z$HT25&Bg<yx$qH=e8Cl6_@-DL=5e$<5E6d=Q9&IHav~`4MeJ^t z^dO2+oA{_Oo_H&G@LbbG&p!updH#uG<#IY=3!;e^v{z8Ktn6hVBP#o41SXb~1lN8# zxG>cTzea3sW3v?-+OYfU$VwO7Z=`yYr=MlT`6P1E$~Ou5`Oigv%VE!tZEq9Z7U+wk z4ceJT3Mmvq<*Cknn_RX`=jZtA*$-hA2d0m<V?<xiVZG#QZ`jdy*&$}Q#G$;OI90AV ztnJxoR#|gkrm_Xo81|<34ll#Ztj)G`yY>%Asa0HEC3>A6o{Sw%X`AjcomI#+QETj_ ze!!rLAr17Wa6jFLY&0UVWvX7E0Hg<%nYG#xSg?PCXn?i;i?y4eKJ4O}7o6`|Pfuf< z(y_jvs2D9Ay)pJXZ^4!ij2BPwqSoDDjkSL(o`su*Qz_~jj+u=0bT!YP#o*Bh#z<}f zmYToSI#D}b<y?Ij7|^z%?u^`8J!0c^@7(S(a(jU(*CM50S@($+0e|(!=3iI!N2{ne z>cr@$LzjpyEN%Yv@uPxILGUs2uM3Wf@|~Sgdd<K73&>;VUwKK7G5<OooM8Sn4-YBS z4m9US2>mzaUlrEgf1ZE61JU3Xi~v!((r5E|)coroJ~66X3Ka?lFiR@tUO5N*k~<HE z0rRir*8D4HMkhqG>WkU4fF8rqGG=mh)R->Cl&Y8?*4s!o*p)@!g3;6w{o1gv^V;_C ze$1H?V+<(O5iep(IsM%6U;wi_H-bmF*Awut9Pqb63@8~Wj@+CayT0M}5#%iC_ZU8T z)uCUZy+Vzo@P}XuYsy;%ywiUz;?6-_=E2?=;B*py7)=`<<!lh=^xXpB;C-LJ2G-^l z;4pnBiszT0*w!@D7z7v&b{!-ux(w=5Pe;Q&7o5ZEhn@-kr&*uJPMvLRW&Is9b@m$2 zW_{61W_>yIt3itf9`tT?=V9`U?58Hro`>4g@$LtuPn$ej2}=5Yq@;gcjs(5^>-mhZ zoDtN4O}~e!;{+k(F^Tv};A4*&0=kBXRv=(6vDK7DQxp3m%q^#Rs0P*9_6QesoC`ro zy^>br4p@!AIou6_EukXLOYST@#g<UQ*b<6IlXgB@5@yGm7QByfgJD!~d~E%iYl=}w z#DGx=el3j3WvKjiaFck!=<l>`s05J|JIat0?G(FGk4`F!q<**dJQ9YnbEp}1XF*?3 zx*rJ9NqCiMHhm(*2js7S#Fh&E3V{E%yzLK*@+!ak|6JbdEvA3EypJSO<?U!mEAR1E z(w{ExjUp+^yABDDD(~-rIHtVIfJ~KF3~exJmX*jc^bJ;Z6(VqN;K{P`Dfe5e_vUdK zZKw7C0urBMN>1!`7yg-HZ-2nNsF)+b=J{n`uR=@bI3ZMtrpMz2d5LUe-?kJIExy#D zUY*OODZi$c3<`E(iRx-Z70%~%cu~h^d)|5ms)(7fLD(Ff$iA!`JN7~dGWOpGzV*Dd zueBJEv95HJX_)MI4oB|T)>e|ov4a}`7RMZJIIz1f2=u}G1h<{@*>8xZj_r6-=?2Ce ziJ00F4ExcrLf1eQ41L31{U$N?J~OyzkiG6%FjfB|{uoUQ`|i+u@-{%ABR;~;aoU>( zXe4&es#Pw_me~+ahU!@)460WnrWgB()tnu8i>(0`?wMD47iXE;F|`MWG0Ug8-5g<X z{M!Wb&ckSI=wv;HDH_^LlH3X}g}K!ZEsIjO=1H6Om)fu`;{k-|uiy{BETcRL9UEYU zA@M2uwcFW1;!FOFcWd-M9=RnRlPgMcds~kpgy`o$GGJB;ai1(sKI9n{1(XllX!W~+ z?HNoXOV15h%r-{T!a;3g9b)2qaB7IBl|iel9ixA3<oa;4(AjaE%h3^^W41V?8in_R zUTC*3G8MZtapI{jVt2<HAwRK)2&LHfoS0to;#d%NH6VXPH<KOEwltW$*MMkjFo<d4 zxIn=*kwSCHT5EA0@aSa9aj->3=U1HN!^uD{M-GU#A}8ca_fhnI>>E?v;2^Ub+<{!! z4fd7RV$6!(4=&&3_L>QJ4_$;TaN>kc6jqi416xZtbOs7_$A{R4A_uW$3>^5}1yZO4 zDb%V#i2<fm<=~}|-+NN%Tym@SzijTH#W?|89g|g5!YaNOTc=Nwl~b!1V;t_k0i(j7 zKB0K@VXD;WP~(KA99d}BDr2U2O7i^I3%|Zq8v~Igo0t7iS&9Dq{wIVQ_)(u&3n(bu zqb1;^ig)dgcuNz1PlcBoLYKk|L}G4`gD|F|RW92={jjtcBe+^BqUs3$6W2p<TU#AB zgwW>8MJ2gXcpV6cxo|1;5?&!%B)5qR3`}{!2Th1v`s-haCs@_4O!4@`Ayig8#hU(W z(eQ<3Gun&idYs?oxj%G&Ji6O~Zgu~aj_Sb4V3vTzEGOIWUsKUaEN$Ke@!`0Msm(k6 zZ}XT{2kM$e5pxYYOx1)xjF=<<eeFRG1`-X+|EL`Cx_zb^VhV(+F<cx~jdsv>z)oNx z8*AWV51m-d_PVXrY|fh){#hBw!Mz_YM*Cg}O4wVmbI35GQn(aYb;N|Iuy|)d4~J*g z9YGfL4!B6^CT}QdjrihkhRM5!$^33_;bzWda?vrz$ZMo9dB_l}DeHvMEl26a*z@S) zPfi(Csx+!2mV+dS^rK7G7DXnvx<?5j=#O$$=W!3w3gWCRazxjC<UBLkACS!QW`4_L zIS&A%ju<V-dBow1dY-_DNu)fBQJkwE6vV<BbOw8GObDYgbLF-Tg-EZDm46fUA%%G; zPw&PIdSxvvE86u~Ak4l9+r0ol&ZF4JOh$3hC-KoAPd}fSs9KIEKX_eWzM>1N!7CDu z#OqYNYMo*zB!#%UNuLI)&=BO8mw2$g{YA4r&_DbUZ0YxkVEBuekQj_TE^;gv1x2O! z5MwE03{H|?v{GNn{7-_Zq92(=;<PTHoYC28sLzMFgutWhR8QJ-<2?Hj(J0s=a?u<` zix@!n4uI7n(0th*(Acf^@bOy^lWE<#$?aWV<XQ6!;$qH`Sf+ZHHWV?LA^fo(WG<7V zJ^Vh^9)|M-VgcI&7NjTeU^33@n605WIt&sprs)uClsQ~s`!U)>5y+Wagrx9Pq4_tz zX(%-JYpNq0CPbSANsfeAM_CF!1c}uVUp*k=i&H2F9m4Gr3B(Q_!h{;H9u6LINTGRn zQAfN6wMXi!sej+RiPRI^1vp|Og+3$soX1IOqkkTb6_5(I7A~1)4BOk+BQ|;il)YYD zwg5LF$^+0izKq)a6<84x=4oaJ>JIVHq4|G?=^vkR2xrB4;#s>3RFZAr29!CZIs_PF zr@+Z%L>+Mh*o&2eIZ(Wo?B$dKqmDSdo@^h}f`%}P^DfnBLEOb^I3_E=jDDr5jHp3M zMW=q5_89t&`ZDHV(I9G(kfYRR`9y0VA+X8)4Xg3W4_ZSV9!NL@ubh(9J{H3VDYX3u zFfrm!;1G^ih-0Q&Y<>7WN-J!E_5cR^umNUx5yOvaId>vl=Q#vOceo5zi(?}((3Mfy zc@Trl;Pb#uJ$E3gBZh;JWn_jhYzvasb4RH<;)A*#yX9iGX6=SaUKp@M6!la4)5nja z*F*Roalq#2j@dfpOp@j?=OxTx#Iwi~6>PEe2UMsiP5sTFq)#W~+~KF+jyFU6Y`^9V z33oEE^<uJ8RxslSpDar|A6*qX5u6Mhgf|?!R2^<^Dum24p;%TsmXH&U)|>qrRCots z80Uy9zQoZJFeT3B(?_d7EGQ&qI}nGCK^8ZL@9!R0v=h$BIHeO?X8(cB(9t~RG7yvz z8|MuD7nUXC$!2haTM0n11@n=J`r#OO^O>Xaq!o>$`r-K8aqSpI#M3j}yUwr@6(Tc{ z&3^2*{v3(Rk)6l~`Drg&jM3RozT=A&+XFkW8wT6hTE#UA(14Q>hpp<}wZcs>PBf<3 zH-^22Fl{vWZWR}w(+EPQLN;kJE%}>6X$-1)WZD=`4~Bif=!tGEJjy48MU38tIt16; zS5b=+ab^{p(-FwXIBH2t%W-uc;bj|0gDyyS>@Fl+e;kF)?Z{H-JG^$ss)%*=V6kTv z*agJ)>A}7)uuFQyZ4%gEueg5{Sfv;CDS>^c7xqDceUw-_a3*JL%iXaji0$Dld<tqq zj+A2Zd>mZxi35C*ad5#)UC}=xB271pJgR09#BR8Dk%_tH?FPhG5YLln=s6O*K%gM; zIsOj8DZR+VLA8kw@EDwU7t}G!Juxr322>4-`Aqo-ri_e(k2P_4Vh7sG1T5l_=WcB4 z#FYow%nAF6^*3xcF|U5uh`P60#`k|^#!aI3F*pn4Bxak_;hBP^t>Ov@&VXLcm>AG? z&=${n13L8EG&+AV=)g4<L@qFiL}Bo_$(lTnNS|~f`5+>EXogbts1F$0FA(=)Xct4S zXoof9m=>Mokm#EzdaN0Lw6EY0GwgPR!T1%Z1Bbr>Tek^e5NR=Tf%&~L6J8}6a54Kr z3}eqg6yD4+3WjM&ewXquWt2O=L3RG{I`H;)?MGra!kBUpt2*}phc}#O*Y<)<qpz3_ zfydS$j&>&AuooSX)_;yX>-i`4g^_zB;=?{qMlQbC0fCDQ@2CCQK;H_q9Lsr?j1s-V zK;H<oH+F6ceJ|07nF%8JvKY6b7?WA7NcQEC;TPMF*c)O+NMPsPCF2Y-Q<0`TR-Phr z3D5@bFF_^{c5uT2+=hg3?dsR5I7f^1p+iEf3fG;@{x7zGk<UbA{SjbUKUhx4`7j7^ zZ-FT47sGgnbr-GdHDrcLOKR65TQmK{r~(3FFoKkt^Hq^U=qqSw^dS*~`Sdjg@;xGd zXCk)=WPKKJQi$Db^kVpsRH6nW1O@~l&oE>^Z?r<l^F~NgVn_``=9+0DM#$3Rv9)V% z^eHos`380ovA0HtVGAOuOfj%B()-6o|7^y++Q6;@ZGS=GHo2k+aj^%XIS-o=wV@)^ zpO<V39zK2vZuh`d2%B%YO=)kcuLd2$oPcdoKH;l4=Y7j<@9#jkWoS)#iThi)Jq-7^ za3(^C*1U@ecbLRnxz};!47>Tzrd6kX2{vEC(?9|~2b)hfpByFT@<nkTT7z|Z;SZO> zJaoK<dV2-s!rd%9FCRzk2D_5}n}S+FuuI07R5UAbXuEnXBB{;;%TU{IBZ07c=$u-3 z6y*SF;77={I<xM>c6Q{e-DwaGJTF%`-z1F!aPP_AFfKB)*`{3w`+?z-n{z@}!l>gk zhd*0&UT`m(p3@7C)tp8o!ZG{{Rp)TPJRpe9dm4=I3s3Vk{0=N#t8*n{VPtDt&p$zI z)LGFOJZN;b!4AyTadSF|3#m)t&jG#G=j?$yUcm;+tpHk`H-G@=IK<)L?$~)y8c|YF zQ2)t3Tr%<=)j%EnX*Jy@?Z_+8@_%)s4|SeyX0AVj<`K+MK{XdgB1CHm<}3#2Ajl&g zu{|IX&4#{!#N*JI!ldR#g9zqKM6?V>yA9D)9R251(!Df%>KVUAmWfkZofjbvCzQA{ z6}b(?Dn#5UYn~Q)Imih=y4jzJzS4`c9hf`B*{LakLEQcn)Mf@w;^rE@^utT0p${0I zJlow<w?<DFz8_f!b7I)k-8b<Nk8p(FgrrgaJxg7->MMpyAub$=PDC4n62Q3<v3Lq8 zmId|FzJA#<24l#X>U<q3#aKt@j^9@2KLCS<O}`6Z(%BBM)w#(+P84yj00V5y`dEO$ zoM8a%l+rI4>UFzCdY&MH4p=Eq2bgr;3sBrc*y@~Th42JXt8@FWUC0$Q_loNCS)pGD z@EvR}=j9Ok2LO>3?inElAGR)$-wcSoJ9ZihDLfIQF@)BEcz5j5m(n#y3VkZD&R*C} zXdK<KUl5xG%4Ty43HIJbgJy<0@1a8#8I>3aoNQF$RB>7dl{iP7*2zSNoE^+L89Zj_ zUx0uS-ET#&_|fRQUV<F;H&W5(TG6Li(dSDc{?HSs=LU1?5Z$4N5gmtFcA|BPy-MA& zRI!B8kdFruGgs$Le9=6}hk7~S&xUPpfb4hzGRDbhAvpSaoXm+1F{A1)GOGV%5seeq zR<v*)T6{340MxVeA0U_D!T$A=Atb*PJZOI{*Pr!Tb?~4IqfBui>}<qP24*6*m|K0n zLvbGYGpvcT6MyO1zYKENZ=?g{4x-R90T0}WsSWIo^ACWpFb>7UJ~DsS>eI0XDD1~h zn3bOMOj4L*Zk{N6kUx?0<}Xl)gwK<FyDZp@t2uza9;xVV48dY1BF3dsXBBc(ocG|b zwzDglJkEIsUd=SnuOT30ko*8I<FI4J^mVtJCimeu4n7TTl%kE0(<9xn(a5w%g|O4N zln9i@(5XOGIzKI_P`D98>e!;tq)VMUU&IY5U`}yv#hZ2=Si;?W&UXNa>l$9Qw>n2H zHk;|Q@C|}FFComOFNFI=6QXZJRku2GtvF8~<nZGlCQ-fEQ_<R*2Xn6-a#4|Rfffj; z1LuGM=L_D2bU1OGi=KKp7~b!^4k1_vIpeWKyj!Q8;gDW#^~j-qS9F``n_IOHU!p^a zuJ<$16KX$fA-rAt0lHI|)c=A!wDsR}qQE$Q#+q2c6NjOvz#C$yh=FN8>?Js8F?f@~ zU}zW`wl?ShxOBF>fX<4w(dv8!ujP14ww){9vHEjr{6KrK!>Ks8AuKW`d3GaCt{BLE zigO8&+Gs=V!<?g6N4yS{I&d2PDxC+~Go6$0mfs$c|Fyrt?ssu6fWx6=i!wrCoHlU` z#Qt7avMo6J;&~9ushhDGE!fyMa+OONcpKsrH7v<c+Hmitpp<M2?^m35yh~v*`7`3+ zYB9$Ueir%HUj?m*UWY%i8}SEy&smTbl)N30)SMHL%u`o^koxJ2Gf%{!^WY*<ZpC60 zo>=bTI*<0n3pD4Wzgvh>K7e=C$^GXqR6|5kI1kyao-Bnk`NVuT-8}9#-^gbMT^wv{ z=R3Bk{}xX;Lr3qHe_<)Cci>!1Tu}BgWWqQ7XHZS;JvyO&G;Zx@7m5zupvS$Z&_OVP zq)B}sBG9ibz&z4P6KqB`YG<ItNi_XAsACBSgfHf$FrhmH6jz+@J`a_j0Laj)2mT2{ zlaayzS7Yy#>U@oHN>a!mHg&4bCyC@5lMyqDW5BAjhDc`$IYT3|ibyS$#Z;_;s?OVq z{5z2Kzk`UaiIM8n$evTQ^-rW>?;@77Gi*Lv(C{K-?26Mx3Y!3R#|E)ONex#6qk=g{ zo^#;`TK*1|hRc2q%v<ak5Uf89A~jt0-#usc()vV5Saq(g0=0M0M7m>tL(KJXjA6ww zEcXwLaZ25^M;xbz!w3uJJdP}i_31_yvH95NG|ZwNyD<hw;OEE!?d~Ka#*>Uun2J%u z82u3=@lSAA&A<y%fpG?Y_a_9#5Lj_E?M?=M%)n>5V{PO$2MG;c`^UHj(8DXcrhR{M z+4A)Geikj&^NQ#JX<5!@gjOTH{xl+Rz?v5?w&~ddu<oA~_pIf{=uu<xLp%H|V!{lc zp<qRAx(f?&?8{-Zto>Hd?c65Ki^8S5__Bi2ETeyiMjg%Fz{aS*hC-OmKibE>v;hI< zud$BvrC5`cBY!PM<;#(<4ONx8qP3Z4NS1xwMfXhkD{pd?m9WM~Yq$fCHt^IM?#3Ir zYjqArNsYIQ@CJfVjYk<#RwGPZS*lzv+i~Tpx8D(8QK$3(oJm_*{<~%G9g&y)jS;1v zp>K-JvP%yHnCY@Z{eh@+;g2khX+-XVTEtNe8n*M~U`MB%zkxI0*rbBvF<*2_p;1V9 zZNxtmEWZouUDog=wuA1~+9*=xNl^$@<b|F@R>KD3ucB1ylO*qpxEIKF%(CseHZt?P zF6}|4R3BwTpYkV092fDQd<2_OPC_a6N%!B2?qpoWF5Itz?JiBl*xmGlzf^TzgdOX_ zt|L<DF&Gh8ga=PWywUvth>aTakCRtQYin$c7-j!Gc=+b|C(0G?OVRsQH5A*5Fg0Bc zPZ1s$Hje;7PvSuc%U5w&_#LIv+UGl<M&A66;Ne_(Wtr-0#scJFL_wOT;|MCNfM5nR z17nlz@v{7uviuJtC12BpC{xDqX!HqSAX@tx;!3dwZ)8-4Cz!l>{(!QIk1I_+D#^$r zU_>Nr$&gp#?w7ikslPN9s;GM!yZtCy@Nd)v%=PnUrVO<J)|NQccbU)~DKr5AWx;PU z%7S~c#!0dBkCNCY9NykMe=`QZ)pZntJ<uXWR~C!XFL^U~7?fM!e!$&-`x*EaD2E5t z%L{mdEgAa5FjLAFTy<2A8o;sZM5df<!5u`^2RS`jeegJQel(zka3<k1)Pk&xczh1V z4XSem9@Qglfi^Ygla(&pWjSBquiWZf06ahUUV%uh#8u$7ivjHs4~*k0gmFQiGH{}q zTv2l#6Um=7k{6lD7dPNu>fZwg*MprGHXQv1P&5hg#Nz$n#$5djRE%=miQHP8zhvC? z4Edj|SIf%|#!k2ndF7zu{>N0R7viK`=sMJxwAR`52=384m($DzLX7p~ZM7X+GzQ){ z8)69Kwt|7z@J(sQ?FTd^>cHoIe^eRo{G(CE@F3WQ!i{h+a<cy@eCCICp<6B7*y`+B zWyo9Sl?1`jV(-insnyv5JgtfT94c7~ufkudk*QaKeWFR=z|$5Smon~)-q?hCmz4wl z^HB5tyy}Cu;#du2Yv=?tfCtm48oWLrur0LF->0PyL$UY{y$H_Y^?nD9MHLKp`*P^R zT{CBMWORd_z2u)DnxHl-a4Nz)7y{1@NPNTa$scg?4dW-AHb*Qg@%aLO9a8(p1hw;x z_(*BsJsB|g7cefz)fUxRDXLR@sD@{P>iY9h9{sPBwX#<(I+Dni){aTmWXMVEL_QSQ z%fcjEt1pM=)Q7U8BOyf}Guo5%;JZjI+SFXoQk~15M)yb=qfLQtZ#&zU(W33aN7=AZ zrOxq)1%1a~wmUtDYDzjU0T{az{f)L5Zm^yKWSJT;eWO!>!bM4XCA@lK3zLb2&`I`6 z{}b(Q@g!~f3}^r>Q9TPIT0d{*j$k4JyQl7FRtL7}@4?eBa@1C!QxkWe!7k`Cz;l^8 zk?lVFQEDtUgpRZ6&Gg+j>QOV5xR+QTE#jgqeJJZ;=}_VYQAm!55>JU&^gA~Fcq<M& zZ2bpFRD_`=+VpP)G_FhMWz>cHV_cWew;EA8jhh~KL(mXc8HQ)F;>pr^Nd*XW#b7FI zi4MGW8n3uX4Qz=_&ako+3`iCPLt|X?zW!{!El0J$(^&}3j`>kp^``)9IBI*IFda2* z-&1&7&Z>MBjPQ)7O@9^%Ee&4Wqrt<2+TxT38+f6?gA5HeTLZLvuA#Tk?!bWciv?4l zXxx`4r_mmqqP>1FVo9N`s7JQB{GNb(5zv5upf)wtMygV6q^ISrr=gWXze7}mp{i8$ zxvA(C25V@j>m4Hcorqrd49YM3JD>6{rVCp4J-6P;P;uW1Zd(a?7;AM}EF)5%qaqDT z<$QTcOrukx@Q*RLx-%6vttSlqB8|YEDUPS5IQ~_N<3PXlS;$a-0~%t)AC;nYY(6tn zVaMh(^FQUopNG_aseH~((K<FC(_Eq2kIkpYRQ^PHj$=MM(UMsn*1SPq7)+?!=pW11 zR5z0%@--zyb?di&G}Z0lG>VrC`3Z`-l&~<_NM$OHk?L}$x-~7;sH0N#$nvOEqnPS^ zq^f(Fs=+DW9tEP;*P}o-#PTWs(Ef=*r<hlD9{K}rZ|S!_gA&*QYlW8jkGs%PWss^r z6QqomTJi_9PsFilWsh6yCCb2|wXn(~hT*TQ<P?a<=t4ZSLEoXF;c}5cU-&6O+^Y(* z3qV#Zd<crMI%J@Iy%NJg*o|c3<DMVKz?<0bEEZ<5x+Atd8$Xj#8C>FUiACB<mp_jS zvDM&-3$ZnLav`>su=xWS!OKsnBk_43;TW=rh-HDY$KXDW!`d5G9WiAD&UXd%Jnc7_ zW#>u~=WhJZd9dPyBSV-Ek;GMH>dIp2I;2_6N5WNGG#(papBAab-drpXejO_StkwUX zcC-nk=sDkv++|@>GkHJ)ByYn8x-7WBUN<f`<Ef?aMx`WMoSslh2KYxy>-rlPVMZ2V zH`%Wo!QBpAsHu#1NhR$(PWEd^j5{L`xF}DYogN7c23)l9)?LZifd^h}iI2E$`7ruB zeG(Yy8L-ucljG~S4OUk6`HN(wi{(@u&7yR?U%O~Q7T3AqU&RCal!NsRs4Q$pqqMeT zro~Anlr6V3-GT#1dDveRwLvaA28Ep5#edvikN2cMzZY%E91@Z~ay00RsQa35vOG0P zER18P^I5$TXE7DFFdL}~<4iIDNqE1)1e^#6C&l(a{|)LHUq|5IZ3~RX9kn8yLlSk6 zQNELb5yHb6)>~0qaDH&NI5+VIG*5B^?jVqs`~yeKTu~Par4@-N%GSW^T+GGNmbilW zn-~KXXAl`_Ykvoo8js!BBAi9UeSP9&9S*E&JYRxSU1woYsc>UGMI7y1LeX#6O3}F@ zDEF4acvGx}4MGJeqn&+DMN)Id+=*60$s4U^J0q=q6b<H+^ai6|p%<b~SXS|WBJMt7 z$2}elU{x&_(_RB?)fgOM`ioE5STv83ur_}Mg`i}{Ize2jIS3s0at-hpH=MF_7nx(X z#;srvPFhVnaO!1@<uYT@AuvqEND(J?d^3r?1T+rfE^?TMaO<;yNS&Ki?#AANyB+Ff zm8~TX8j)|b(}cKV1Ce^cT(FzX1k7pz?zm?-${vo#3K;%Nw9@Qo30{MTvIE%LSi%!< zuE0rHfqEG6l#)T5t`N7kOY7RBw<0E{DZZU1R=Zq?AykiXu!tE8AXw{XVG~+fd%^xy zWA)gSFIOs2pJmD+%wfl2Lc|GUl&iP~o0fPs(Z!>O1Im;R&w7@iegOVdV$xA8Y`P4{ z>pmHWig7mw!fZy3c+@e2JnE%M9Q4AAGA57v=(In9DR4P5)jn9KKbk)>gq1MzG_&hV z>Y%AjGN|ODy<%>K2Ghek@le@b_Z)8Fo6K{O<?uYXTeYGAO$u)}GPoFH5|6S8dvbhO z>jf4@xE-A1nom)k9!%i^EC56uqqBvxmwvY&r$adBDpS!g(K^ScINV|dmmVHp^6(Td zbiE)UZ~+mSJbMNaG0&b!d3YcX59P7IqZVnKABISlZD1)-9?uQqlvZO))jP&r37kG! zP@F0p&l7_moO;71E6}{r#xM{oXM?E)MjW5U6L=$4xknv^%_*o#9*4$*(es#CteEjd zl$S3fnMk|+5z&xvF!&vSQm6pZg^K1vJEibM063~EM`pU`EV%=n!5kg~zR75>I1GF( zP*Y)UaibjMT2z`Xg`NWyY`^ypz{V=*KK5~Wk{`19ac%{@%D+1i{1@zZFE)<@p9xcJ zo&$#Zcith?zdr<MSep4@8<u7Z@V$=%`+kdY)pqnO+73j*abPL*9w6f&@OB0^0)+g* zb6!ztmK63Oub$J)5ilx_qfX_PE~5;%?j{R`P%cAE)F3M<ZHE>Zxt5HuYc@=tIQH9A zYc<wmQbd=c-RkFp8V?%JKN}QsNP#M6P!K0}mw`g!EgWN2A#&{`6o+S1pFmo1HuXdl zMpy&hBhUSL$|jHUF}5od;;LwDvmK{EvBX|IGOYHIBg@^B@#ce4WO>9q0QBH!B~t#u zQ^zhJuIt5~$r}+{j^IcbpUNA?^H^;XN(QgV(kd7gukx0ADpK)w0_JHo5HF4_PX~+( z@CGG??~-NnC}RhKghLq%59M3voJYOfRATL^hlK5X8D*AgE=CLFmZb7}Hig3dS^*?L zi(bxC(qytHB~SH}ea6%lK#=Gznx*BJfIN<;r)xP^X1If{Jotko-M<z|JnA4^TNk;y z6Zcl{KC37pJsrpOrQuT$(OYDc%U?(>e@9kVxG@_Kk$xpd-V!Ah`o8e>W7ULr{&|Yf zJp?vD^&EW73aqd6z<+b#?EDl^r!SX<IS8KifKD8o1f8JWHFTmCLeTDckc0P39IDT6 z##E+#1G#?SDyWjtdUCm8;d^K}wm<U}y}k+Q*)4$H3#4~qik>(<e+KCNgw)0e*+XiP zO>9n&K!~C_{XW&4YLQs40Vwj8LT#Xpd^|_z^H1{q*T_${hRWbIeH?mUkU?4})Anjf z3z0U_C>jln!56m+W8gsEhB3Gcd@8t~AF@L|XvZS}&Lxi-iMFEcwz5tHg9lHVH_WhK zS`(7dX?7jh8s4n90-Fu#`<pyy_lY&IKHq}zL<CIDL6*H6=g#FQ`vs7@=^uJ-5FKin zasVx`O!+9a&3!>0_3I_~WIRP<;**<Zt;WZ8W7ualB<x#-pNMUO=)-W*gBNKb{AjZc zM6nGY(YinmtqVK<@xl)qZh4;7J0oB^0DicNi(3js?;x5-9+3E|BefeKBBO!)1~^>q zi@&LEfCGAHnP?;PE;Ad)_|BfD5eQ(>0HO!$p5({0_Uz+&Yw7%ELrcQ<B9K}-6T&mK z6xEu2o2b^I5UWwGkANrbOjM>&x+@Ubu;Hgu@C5(`IX^BO%xS;%D8&{E=T*}bF0Z%2 ze(LBHqA8q+6y}c{<Sb$M(Q24@DVo!Q231bB4du|ict5)S3*(~tKW6pw0wi|ogONRt zqPfIVT#bf)9epFJnN>)<sKNnw;m2?$5W+*R{6J-~rME`~A!+UX-#|27w#Bog5U*kL zsJCG>7`eMs*^Qc6c9=CQ{azTZ<p_>U%*5TXFzm)<ALdOT4}k`}`6ApIx11dxdTne) z17}8$x9~SGW8^-qx6@R}UsTCmtda{L60HVg48Qc4=q7=qZeqh~k=9(p=05u5Tj)bK zK%SF92;;P&7OBMRsMrHHqZ-ZuV}=E+VP<zLR=4-t9(PylSIzROeLe6nyh@yxj>NNy zI_KxZ*##imyA&C>mzQT$w%W36YF|V%;wXK^ZcL8%N7o=bWiQsN*D#}Jtc=8T00(Lw z<vp*`I6iW-1Kw<wdPT4`lLOIi`xfLhJh-d7tfHMOY4&d`y~?#2mC9NCO)_mE2pPVQ zh>O`^BTIPj$n+#DQ;nGpwK7e7+EY5Z@aB~h;{J(t<LU|CNC8F$tEVXMlqo-WBhxZ* zLyjl7vzyZZ-ClbK*t$bjK9(y!!QLkDhZR+y{aa0g@IBaXg3Fx?_vi11`;C$?*uI^? zpMVG40!wGjAmcGuPOYf+TvZRz3}zv&xyh7VZYv5g9rO*)<nM;DCI66^w63o=QVb3| zx;;1_S=707a9Kqhbt5XptRO^6RZeAk?d-nM%mYijHqBw>A!-Nfv#1<YQCj_gP-Fp% z6>*$}tS-UNFZ_7@eDZ*B0L}8+w^a73p<h`US2JT3v5mr|)eze!sz&^llK9=L4?P(E z**Ou^Au3DxJ8KG(F5Hi*!mmJOYI9KntST(IR9ZI?F6W`lc?HtC#-c4t^QCp;aLHMN zx2R(&WY$ooV3#I>46)=l;Sv>lGg2;X!2|$KSz8O;I2eFmAW6H#19UigeDwwhZfmsI zXkmZ9pDphv(^y(Zed~~w59NxFSPJ~q!Bn@V{GEmA<k+H%Wnh_5W|rv)&L3${LpChZ zSms<AJeWCe6!=GxM!><&KzXF6u$EL>^riF!ucGc(R?+N9HkT<wS`h7+;?<*gdHAUf z%!8!;IuCzf1!d%*mC9$9@ty0_%eX5HkD)nzx&iIS+CKoM@p~n>3FrsTB{{L`6BuAv zuas0kIk%}0Klg$QY4CfRlo)@&0s}!zy}XYrD*f+b4HOGwSU-dnTuKiS=Sc!}fMKnw z7r%mE^&4kP{Kf1XMEm`B`S(<a!O2niuMhor{vs-wC~BakCj;w{tDpr7139@wLD&ZW zL;3%0{->f=f2{oJ`5Omn0_)@-XW)O8|N7v6&j0^r`HOxcf2;nDj#kuv<&PKte<%O- z_x*VJ&HSNoR`u7_{6zKtC;9(-{r~snUyA(G^}jlqr2bPBiI;lp|Np%G<^P-fg9lTM z?@c*)uzK-d3*!(%;`MG!-(kE6?}EJ~td=G$G-el#&NKM6?2F8Hzy-r)eaeT1%k^qj z?60u#*k)CQb(H~bX>B7Gn6Z8hLTLH7TX4*PuSwmo{ySC?JYVrsgyB6cnf=zoF5t#} zm^=KEsIhc@C3exkO*YPfOn{iMmd031E@~;UyApZ9AF>7PKeLWA0Z1pV6vWMX#Wfe! zl-M!ln1Qj)MmAO)o1f_}Fn&X1Bu!#5n!4NsuJI#z*jjE(YX}gl>;SnuE5J;h2YR@l zS;U+rg$Y|R7fRuCEihBh#VfdyLO~W7#mY!8R)#1RPBkMIO5ia0BP|}b`ij>_UV|{D zA>wHtb=>@X{1&XIj*C4!i-+a=SK<+`y?soL;{t21LEuAu5K>ehyeQB&*&0k{F8NaA zi1h$2%(rA>Ms){e$XPsX`U$KSgQW+*!hlcWA_Z81>s8{=-v%b7e?O@Y_;E1n9xr7d z)B!&U*h?9(mj<~>Yq4$LP=`)R1BH7<pbZ?ibrJVCo~hsJSc*!9THs;|aToyG+{Eu` ze5>7#MR^RkngUnxS2^%=KvL*)Bn);z)?eeLvb8iLPt2tKyetyHZNr!(DBP_5;vka+ zTvDtw1LHGCQM(j;1JU1vZ19W0d&JKo+lDDy4CBxKtd~oGwSsKX*!?bV%`{s}sU6NJ zbL05XateB-5⪚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 literal 0 HcmV?d00001 diff --git a/misc/ss.c b/misc/ss.c new file mode 100644 index 0000000..668a5bf --- /dev/null +++ b/misc/ss.c @@ -0,0 +1,2803 @@ +/* + * ss.c "sockstat", socket statistics + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +#include <dirent.h> +#include <fnmatch.h> +#include <getopt.h> + +#include "utils.h" +#include "rt_names.h" +#include "ll_map.h" +#include "libnetlink.h" +#include "SNAPSHOT.h" + +#include <linux/tcp.h> +#include <linux/tcp_diag.h> + +int resolve_hosts = 0; +int resolve_services = 1; +int preferred_family = AF_UNSPEC; +int show_options = 0; +int show_details = 0; +int show_users = 0; +int show_mem = 0; +int show_tcpinfo = 0; + +int netid_width; +int state_width; +int addrp_width; +int addr_width; +int serv_width; +int screen_width; + +static const char *TCP_PROTO = "tcp"; +static const char *UDP_PROTO = "udp"; +static const char *RAW_PROTO = "raw"; +static const char *dg_proto = NULL; + +enum +{ + TCP_DB, + UDP_DB, + RAW_DB, + UNIX_DG_DB, + UNIX_ST_DB, + PACKET_DG_DB, + PACKET_R_DB, + NETLINK_DB, + MAX_DB +}; + +#define PACKET_DBM ((1<<PACKET_DG_DB)|(1<<PACKET_R_DB)) +#define UNIX_DBM ((1<<UNIX_DG_DB)|(1<<UNIX_ST_DB)) +#define ALL_DB ((1<<MAX_DB)-1) + +enum { + SS_UNKNOWN, + SS_ESTABLISHED, + SS_SYN_SENT, + SS_SYN_RECV, + SS_FIN_WAIT1, + SS_FIN_WAIT2, + SS_TIME_WAIT, + SS_CLOSE, + SS_CLOSE_WAIT, + SS_LAST_ACK, + SS_LISTEN, + SS_CLOSING, + SS_MAX +}; + +#define SS_ALL ((1<<SS_MAX)-1) + +#include "ssfilter.h" + +struct filter +{ + int dbs; + int states; + int families; + struct ssfilter *f; +}; + +struct filter default_filter = { + dbs: (1<<TCP_DB), + states: SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)), + families: (1<<AF_INET)|(1<<AF_INET6), +}; + +struct filter current_filter; + +int generic_proc_open(char *env, char *name) +{ + char store[128]; + char *p = getenv(env); + if (!p) { + p = getenv("PROC_ROOT") ? : "/proc"; + snprintf(store, sizeof(store)-1, "%s/%s", p, name); + p = store; + } + return open(store, O_RDONLY); +} + +int net_tcp_open(void) +{ + return generic_proc_open("PROC_NET_TCP", "net/tcp"); +} + +int net_tcp6_open(void) +{ + return generic_proc_open("PROC_NET_TCP6", "net/tcp6"); +} + +int net_udp_open(void) +{ + return generic_proc_open("PROC_NET_UDP", "net/udp"); +} + +int net_udp6_open(void) +{ + return generic_proc_open("PROC_NET_UDP6", "net/udp6"); +} + +int net_raw_open(void) +{ + return generic_proc_open("PROC_NET_RAW", "net/raw"); +} + +int net_raw6_open(void) +{ + return generic_proc_open("PROC_NET_RAW6", "net/raw6"); +} + +int net_unix_open(void) +{ + return generic_proc_open("PROC_NET_UNIX", "net/unix"); +} + +int net_packet_open(void) +{ + return generic_proc_open("PROC_NET_PACKET", "net/packet"); +} + +int net_netlink_open(void) +{ + return generic_proc_open("PROC_NET_NETLINK", "net/netlink"); +} + +int slabinfo_open(void) +{ + return generic_proc_open("PROC_SLABINFO", "slabinfo"); +} + +int net_sockstat_open(void) +{ + return generic_proc_open("PROC_NET_SOCKSTAT", "net/sockstat"); +} + +int net_sockstat6_open(void) +{ + return generic_proc_open("PROC_NET_SOCKSTAT6", "net/sockstat6"); +} + +int net_snmp_open(void) +{ + return generic_proc_open("PROC_NET_SNMP", "net/snmp"); +} + +int net_netstat_open(void) +{ + return generic_proc_open("PROC_NET_NETSTAT", "net/netstat"); +} + +int ephemeral_ports_open(void) +{ + return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range"); +} + +int find_users(int ino, char *buf, int buflen) +{ + char pattern[64]; + int pattern_len; + char *ptr = buf; + char name[1024]; + DIR *dir; + struct dirent *d; + int cnt = 0; + int nameoff; + + if (!ino) + return 0; + + sprintf(pattern, "socket:[%d]", ino); + pattern_len = strlen(pattern); + + strncpy(name, getenv("PROC_ROOT") ? : "/proc/", sizeof(name)/2); + name[sizeof(name)/2] = 0; + if (strlen(name) == 0 || + name[strlen(name)-1] != '/') + strcat(name, "/"); + nameoff = strlen(name); + if ((dir = opendir(name)) == NULL) + return 0; + + while ((d = readdir(dir)) != NULL) { + DIR *dir1; + struct dirent *d1; + int pid; + int pos; + char crap; + char process[16]; + + if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1) + continue; + + sprintf(name+nameoff, "%d/fd/", pid); + pos = strlen(name); + if ((dir1 = opendir(name)) == NULL) + continue; + + process[0] = 0; + + while ((d1 = readdir(dir1)) != NULL) { + int fd, n; + char lnk[64]; + + if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1) + continue; + + sprintf(name+pos, "%d", fd); + n = readlink(name, lnk, sizeof(lnk)-1); + if (n != pattern_len || + memcmp(lnk, pattern, n)) + continue; + + if (ptr-buf >= buflen-1) + break; + + if (process[0] == 0) { + char tmp[1024]; + FILE *fp; + snprintf(tmp, sizeof(tmp), "%s/%d/stat", + getenv("PROC_ROOT") ? : "/proc", pid); + if ((fp = fopen(tmp, "r")) != NULL) { + fscanf(fp, "%*d (%[^)])", process); + fclose(fp); + } + } + + snprintf(ptr, buflen-(ptr-buf), "(\"%s\",%d,%d),", process, pid, fd); + ptr += strlen(ptr); + cnt++; + } + closedir(dir1); + } + closedir(dir); + if (ptr != buf) + ptr[-1] = 0; + return cnt; +} + + +/* Get stats from slab */ + +struct slabstat +{ + int socks; + int tcp_ports; + int tcp_tws; + int tcp_syns; + int skbs; +}; + +struct slabstat slabstat; + +static const char *slabstat_ids[] = +{ + "sock", + "tcp_bind_bucket", + "tcp_tw_bucket", + "tcp_open_request", + "skbuff_head_cache", +}; + +int get_slabstat(struct slabstat *s) +{ + char buf[256]; + FILE *fp; + int cnt; + + memset(s, 0, sizeof(*s)); + + if ((fp = fdopen(slabinfo_open(), "r")) == NULL) + return -1; + + cnt = sizeof(*s)/sizeof(int); + + fgets(buf, sizeof(buf), fp); + while(fgets(buf, sizeof(buf), fp) != NULL) { + int i; + for (i=0; i<sizeof(slabstat_ids)/sizeof(slabstat_ids[0]); i++) { + if (memcmp(buf, slabstat_ids[i], strlen(slabstat_ids[i])) == 0) { + sscanf(buf, "%*s%d", ((int *)s) + i); + cnt--; + break; + } + } + if (cnt <= 0) + break; + } + + fclose(fp); + return 0; +} + +static const char *sstate_name[] = { + "UNKNOWN", + [TCP_ESTABLISHED] = "ESTAB", + [TCP_SYN_SENT] = "SYN-SENT", + [TCP_SYN_RECV] = "SYN-RECV", + [TCP_FIN_WAIT1] = "FIN-WAIT-1", + [TCP_FIN_WAIT2] = "FIN-WAIT-2", + [TCP_TIME_WAIT] = "TIME-WAIT", + [TCP_CLOSE] = "UNCONN", + [TCP_CLOSE_WAIT] = "CLOSE-WAIT", + [TCP_LAST_ACK] = "LAST-ACK", + [TCP_LISTEN] = "LISTEN", + [TCP_CLOSING] = "CLOSING", +}; + +static const char *sstate_namel[] = { + "UNKNOWN", + [TCP_ESTABLISHED] = "established", + [TCP_SYN_SENT] = "syn-sent", + [TCP_SYN_RECV] = "syn-recv", + [TCP_FIN_WAIT1] = "fin-wait-1", + [TCP_FIN_WAIT2] = "fin-wait-2", + [TCP_TIME_WAIT] = "time-wait", + [TCP_CLOSE] = "unconnected", + [TCP_CLOSE_WAIT] = "close-wait", + [TCP_LAST_ACK] = "last-ack", + [TCP_LISTEN] = "listening", + [TCP_CLOSING] = "closing", +}; + +struct tcpstat +{ + inet_prefix local; + inet_prefix remote; + int lport; + int rport; + int state; + int rq, wq; + int timer; + int timeout; + int retrs; + int ino; + int probes; + int uid; + int refcnt; + unsigned long long sk; + int rto, ato, qack, cwnd, ssthresh; +}; + +static const char *tmr_name[] = { + "off", + "on", + "keepalive", + "timewait", + "persist", + "unknown" +}; + +const char *print_ms_timer(int timeout) +{ + static char buf[64]; + int secs, msecs, minutes; + if (timeout < 0) + timeout = 0; + secs = timeout/1000; + minutes = secs/60; + secs = secs%60; + msecs = timeout%1000; + buf[0] = 0; + if (minutes) { + msecs = 0; + snprintf(buf, sizeof(buf)-16, "%dmin", minutes); + if (minutes > 9) + secs = 0; + } + if (secs) { + if (secs > 9) + msecs = 0; + sprintf(buf+strlen(buf), "%d%s", secs, msecs ? "." : "sec"); + } + if (msecs) + sprintf(buf+strlen(buf), "%03dms", msecs); + return buf; +}; + +const char *print_hz_timer(int timeout) +{ + int hz = get_hz(); + return print_ms_timer(((timeout*1000) + hz-1)/hz); +}; + +struct scache +{ + struct scache *next; + int port; + char *name; + const char *proto; +}; + +struct scache *rlist; + +void init_service_resolver(void) +{ + char buf[128]; + FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r"); + if (fp) { + fgets(buf, sizeof(buf), fp); + while (fgets(buf, sizeof(buf), fp) != NULL) { + unsigned int progn, port; + char proto[128], prog[128]; + if (sscanf(buf, "%u %*d %s %u %s", &progn, proto, + &port, prog+4) == 4) { + struct scache *c = malloc(sizeof(*c)); + if (c) { + c->port = port; + memcpy(prog, "rpc.", 4); + c->name = strdup(prog); + if (strcmp(proto, TCP_PROTO) == 0) + c->proto = TCP_PROTO; + else if (strcmp(proto, UDP_PROTO) == 0) + c->proto = UDP_PROTO; + else + c->proto = NULL; + c->next = rlist; + rlist = c; + } + } + } + } +} + +static int ip_local_port_min, ip_local_port_max; + +/* Even do not try default linux ephemeral port ranges: + * default /etc/services contains so much of useless crap + * wouldbe "allocated" to this area that resolution + * is really harmful. I shrug each time when seeing + * "socks" or "cfinger" in dumps. + */ +static int is_ephemeral(int port) +{ + if (!ip_local_port_min) { + FILE *f = fdopen(ephemeral_ports_open(), "r"); + if (f) { + fscanf(f, "%d %d", + &ip_local_port_min, &ip_local_port_max); + fclose(f); + } else { + ip_local_port_min = 1024; + ip_local_port_max = 4999; + } + } + + return (port >= ip_local_port_min && port<= ip_local_port_max); +} + + +const char *__resolve_service(int port) +{ + struct scache *c; + + for (c = rlist; c; c = c->next) { + if (c->port == port && c->proto == dg_proto) + return c->name; + } + + if (!is_ephemeral(port)) { + static int notfirst; + struct servent *se; + if (!notfirst) { + setservent(1); + notfirst = 1; + } + se = getservbyport(htons(port), dg_proto); + if (se) + return se->s_name; + } + + return NULL; +} + + +const char *resolve_service(int port) +{ + static char buf[128]; + static struct scache cache[256]; + + if (port == 0) { + buf[0] = '*'; + buf[1] = 0; + return buf; + } + + if (resolve_services) { + if (dg_proto == RAW_PROTO) { + return inet_proto_n2a(port, buf, sizeof(buf)); + } else { + struct scache *c; + const char *res; + int hash = (port^(((unsigned long)dg_proto)>>2))&255; + + for (c = &cache[hash]; c; c = c->next) { + if (c->port == port && + c->proto == dg_proto) { + if (c->name) + return c->name; + goto do_numeric; + } + } + + if ((res = __resolve_service(port)) != NULL) { + if ((c = malloc(sizeof(*c))) == NULL) + goto do_numeric; + } else { + c = &cache[hash]; + if (c->name) + free(c->name); + } + c->port = port; + c->name = NULL; + c->proto = dg_proto; + if (res) { + c->name = strdup(res); + c->next = cache[hash].next; + cache[hash].next = c; + } + if (c->name) + return c->name; + } + } + + do_numeric: + sprintf(buf, "%u", port); + return buf; +} + +void formatted_print(const inet_prefix *a, int port) +{ + char buf[1024]; + const char *ap = buf; + int est_len; + + est_len = addr_width; + + if (a->family == AF_INET) { + if (a->data[0] == 0) { + buf[0] = '*'; + buf[1] = 0; + } else { + ap = format_host(AF_INET, 4, a->data, buf, sizeof(buf)); + } + } else { + ap = format_host(a->family, 16, a->data, buf, sizeof(buf)); + est_len = strlen(ap); + if (est_len <= addr_width) + est_len = addr_width; + else + est_len = addr_width + ((est_len-addr_width+3)/4)*4; + } + printf("%*s:%-*s ", est_len, ap, serv_width, resolve_service(port)); +} + +struct aafilter +{ + inet_prefix addr; + int port; + struct aafilter *next; +}; + +int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, int plen) +{ + if (!inet_addr_match(a, p, plen)) + return 0; + + /* Cursed "v4 mapped" addresses: v4 mapped socket matches + * pure IPv4 rule, but v4-mapped rule selects only v4-mapped + * sockets. Fair? */ + if (p->family == AF_INET && a->family == AF_INET6) { + if (a->data[0] == 0 && a->data[1] == 0 && + a->data[2] == htonl(0xffff)) { + inet_prefix tmp = *a; + tmp.data[0] = a->data[3]; + return inet_addr_match(&tmp, p, plen); + } + } + return 1; +} + +int unix_match(const inet_prefix *a, const inet_prefix *p) +{ + char *addr, *pattern; + memcpy(&addr, a->data, sizeof(addr)); + memcpy(&pattern, p->data, sizeof(pattern)); + if (pattern == NULL) + return 1; + if (addr == NULL) + addr = ""; + return !fnmatch(pattern, addr, 0); +} + +int run_ssfilter(struct ssfilter *f, struct tcpstat *s) +{ + switch (f->type) { + case SSF_S_AUTO: + { + static int low, high=65535; + + if (s->local.family == AF_UNIX) { + char *p; + memcpy(&p, s->local.data, sizeof(p)); + return p == NULL || (p[0] == '@' && strlen(p) == 6 && + strspn(p+1, "0123456789abcdef") == 5); + } + if (s->local.family == AF_PACKET) + return s->lport == 0 && s->local.data == 0; + if (s->local.family == AF_NETLINK) + return s->lport < 0; + + if (!low) { + FILE *fp = fdopen(ephemeral_ports_open(), "r"); + if (fp) { + fscanf(fp, "%d%d", &low, &high); + fclose(fp); + } + } + return s->lport >= low && s->lport <= high; + } + case SSF_DCOND: + { + struct aafilter *a = (void*)f->pred; + if (a->addr.family == AF_UNIX) + return unix_match(&s->remote, &a->addr); + if (a->port != -1 && a->port != s->rport) + return 0; + if (a->addr.bitlen) { + do { + if (!inet2_addr_match(&s->remote, &a->addr, a->addr.bitlen)) + return 1; + } while ((a = a->next) != NULL); + return 0; + } + return 1; + } + case SSF_SCOND: + { + struct aafilter *a = (void*)f->pred; + if (a->addr.family == AF_UNIX) + return unix_match(&s->local, &a->addr); + if (a->port != -1 && a->port != s->lport) + return 0; + if (a->addr.bitlen) { + do { + if (!inet2_addr_match(&s->local, &a->addr, a->addr.bitlen)) + return 1; + } while ((a = a->next) != NULL); + return 0; + } + return 1; + } + case SSF_D_GE: + { + struct aafilter *a = (void*)f->pred; + return s->rport >= a->port; + } + case SSF_D_LE: + { + struct aafilter *a = (void*)f->pred; + return s->rport <= a->port; + } + case SSF_S_GE: + { + struct aafilter *a = (void*)f->pred; + return s->lport >= a->port; + } + case SSF_S_LE: + { + struct aafilter *a = (void*)f->pred; + return s->lport <= a->port; + } + + /* Yup. It is recursion. Sorry. */ + case SSF_AND: + return run_ssfilter(f->pred, s) && run_ssfilter(f->post, s); + case SSF_OR: + return run_ssfilter(f->pred, s) || run_ssfilter(f->post, s); + case SSF_NOT: + return !run_ssfilter(f->pred, s); + default: + abort(); + } +} + +/* Relocate external jumps by reloc. */ +static void ssfilter_patch(char *a, int len, int reloc) +{ + while (len > 0) { + struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op*)a; + if (op->no == len+4) + op->no += reloc; + len -= op->yes; + a += op->yes; + } + if (len < 0) + abort(); +} + +static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode) +{ + switch (f->type) { + case SSF_S_AUTO: + { + if (!(*bytecode=malloc(4))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_AUTO, 4, 8 }; + return 8; + } + case SSF_DCOND: + case SSF_SCOND: + { + struct aafilter *a = (void*)f->pred; + struct aafilter *b; + char *ptr; + int code = (f->type == SSF_DCOND ? TCPDIAG_BC_D_COND : TCPDIAG_BC_S_COND); + int len = 0; + + for (b=a; b; b=b->next) { + len += 4 + sizeof(struct tcpdiag_hostcond); + if (a->addr.family == AF_INET6) + len += 16; + else + len += 4; + if (b->next) + len += 4; + } + if (!(ptr = malloc(len))) abort(); + *bytecode = ptr; + for (b=a; b; b=b->next) { + struct tcpdiag_bc_op *op = (struct tcpdiag_bc_op *)ptr; + int alen = (a->addr.family == AF_INET6 ? 16 : 4); + int oplen = alen + 4 + sizeof(struct tcpdiag_hostcond); + struct tcpdiag_hostcond *cond = (struct tcpdiag_hostcond*)(ptr+4); + + *op = (struct tcpdiag_bc_op){ code, oplen, oplen+4 }; + cond->family = a->addr.family; + cond->port = a->port; + cond->prefix_len = a->addr.bitlen; + memcpy(cond->addr, a->addr.data, alen); + ptr += oplen; + if (b->next) { + op = (struct tcpdiag_bc_op *)ptr; + *op = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, len - (ptr-*bytecode)}; + ptr += 4; + } + } + return ptr - *bytecode; + } + case SSF_D_GE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_GE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_D_LE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_D_LE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_S_GE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_GE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + case SSF_S_LE: + { + struct aafilter *x = (void*)f->pred; + if (!(*bytecode=malloc(8))) abort(); + ((struct tcpdiag_bc_op*)*bytecode)[0] = (struct tcpdiag_bc_op){ TCPDIAG_BC_S_LE, 8, 12 }; + ((struct tcpdiag_bc_op*)*bytecode)[1] = (struct tcpdiag_bc_op){ 0, 0, x->port }; + return 8; + } + + case SSF_AND: + { + char *a1, *a2, *a, l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); + l2 = ssfilter_bytecompile(f->post, &a2); + if (!(a = malloc(l1+l2))) abort(); + memcpy(a, a1, l1); + memcpy(a+l1, a2, l2); + free(a1); free(a2); + ssfilter_patch(a, l1, l2); + *bytecode = a; + return l1+l2; + } + case SSF_OR: + { + char *a1, *a2, *a, l1, l2; + l1 = ssfilter_bytecompile(f->pred, &a1); + l2 = ssfilter_bytecompile(f->post, &a2); + if (!(a = malloc(l1+l2+4))) abort(); + memcpy(a, a1, l1); + memcpy(a+l1+4, a2, l2); + free(a1); free(a2); + *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, l2+4 }; + *bytecode = a; + return l1+l2+4; + } + case SSF_NOT: + { + char *a1, *a, l1; + l1 = ssfilter_bytecompile(f->pred, &a1); + if (!(a = malloc(l1+4))) abort(); + memcpy(a, a1, l1); + free(a1); + *(struct tcpdiag_bc_op*)(a+l1) = (struct tcpdiag_bc_op){ TCPDIAG_BC_JMP, 4, 8 }; + *bytecode = a; + return l1+4; + } + default: + abort(); + } +} + +static int remember_he(struct aafilter *a, struct hostent *he) +{ + char **ptr = he->h_addr_list; + int cnt = 0; + int len; + + if (he->h_addrtype == AF_INET) + len = 4; + else if (he->h_addrtype == AF_INET6) + len = 16; + else + return 0; + + while (*ptr) { + struct aafilter *b = a; + if (a->addr.bitlen) { + if ((b = malloc(sizeof(*b))) == NULL) + return cnt; + *b = *a; + b->next = a->next; + a->next = b; + } + memcpy(b->addr.data, *ptr, len); + b->addr.bytelen = len; + b->addr.bitlen = len*8; + b->addr.family = he->h_addrtype; + ptr++; + cnt++; + } + return cnt; +} + +static int get_dns_host(struct aafilter *a, const char *addr, int fam) +{ + static int notfirst; + int cnt = 0; + struct hostent *he; + + a->addr.bitlen = 0; + if (!notfirst) { + sethostent(1); + notfirst = 1; + } + he = gethostbyname2(addr, fam == AF_UNSPEC ? AF_INET : fam); + if (he) + cnt = remember_he(a, he); + if (fam == AF_UNSPEC) { + he = gethostbyname2(addr, AF_INET6); + if (he) + cnt += remember_he(a, he); + } + return !cnt; +} + +static int xll_initted = 0; + +static void xll_init(void) +{ + struct rtnl_handle rth; + rtnl_open(&rth, 0); + ll_init_map(&rth); + rtnl_close(&rth); + xll_initted = 1; +} + +static const char *xll_index_to_name(int index) +{ + if (!xll_initted) + xll_init(); + return ll_index_to_name(index); +} + +static int xll_name_to_index(const char *dev) +{ + if (!xll_initted) + xll_init(); + return ll_name_to_index(dev); +} + +void *parse_hostcond(char *addr) +{ + char *port = NULL; + struct aafilter a; + struct aafilter *res; + int fam = preferred_family; + + memset(&a, 0, sizeof(a)); + a.port = -1; + + if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) { + char *p; + a.addr.family = AF_UNIX; + if (strncmp(addr, "unix:", 5) == 0) + addr+=5; + p = strdup(addr); + a.addr.bitlen = 8*strlen(p); + memcpy(a.addr.data, &p, sizeof(p)); + goto out; + } + + if (fam == AF_PACKET || strncmp(addr, "link:", 5) == 0) { + a.addr.family = AF_PACKET; + a.addr.bitlen = 0; + if (strncmp(addr, "link:", 5) == 0) + addr+=5; + port = strchr(addr, ':'); + if (port) { + *port = 0; + if (port[1] && strcmp(port+1, "*")) { + if (get_integer(&a.port, port+1, 0)) { + if ((a.port = xll_name_to_index(port+1)) <= 0) + return NULL; + } + } + } + if (addr[0] && strcmp(addr, "*")) { + unsigned short tmp; + a.addr.bitlen = 32; + if (ll_proto_a2n(&tmp, addr)) + return NULL; + a.addr.data[0] = ntohs(tmp); + } + goto out; + } + + if (fam == AF_NETLINK || strncmp(addr, "netlink:", 8) == 0) { + a.addr.family = AF_NETLINK; + a.addr.bitlen = 0; + if (strncmp(addr, "netlink:", 8) == 0) + addr+=8; + port = strchr(addr, ':'); + if (port) { + *port = 0; + if (port[1] && strcmp(port+1, "*")) { + if (get_integer(&a.port, port+1, 0)) { + if (strcmp(port+1, "kernel") == 0) + a.port = 0; + else + return NULL; + } + } + } + if (addr[0] && strcmp(addr, "*")) { + a.addr.bitlen = 32; + if (get_u32(a.addr.data, addr, 0)) { + if (strcmp(addr, "rtnl") == 0) + a.addr.data[0] = 0; + else if (strcmp(addr, "fw") == 0) + a.addr.data[0] = 3; + else if (strcmp(addr, "tcpdiag") == 0) + a.addr.data[0] = 4; + else + return NULL; + } + } + goto out; + } + + if (strncmp(addr, "inet:", 5) == 0) { + addr += 5; + fam = AF_INET; + } else if (strncmp(addr, "inet6:", 6) == 0) { + addr += 6; + fam = AF_INET6; + } + + /* URL-like literal [] */ + if (addr[0] == '[') { + addr++; + if ((port = strchr(addr, ']')) == NULL) + return NULL; + *port++ = 0; + } else if (addr[0] == '*') { + port = addr+1; + } else { + port = strrchr(strchr(addr, '/') ? : addr, ':'); + } + if (port && *port) { + if (*port != ':') + return NULL; + *port++ = 0; + if (*port && *port != '*') { + if (get_integer(&a.port, port, 0)) { + struct servent *se1 = NULL; + struct servent *se2 = NULL; + if (current_filter.dbs&(1<<UDP_DB)) + se1 = getservbyname(port, UDP_PROTO); + if (current_filter.dbs&(1<<TCP_DB)) + se2 = getservbyname(port, TCP_PROTO); + if (se1 && se2 && se1->s_port != se2->s_port) { + fprintf(stderr, "Error: ambiguous port \"%s\".\n", port); + return NULL; + } + if (!se1) + se1 = se2; + if (se1) { + a.port = ntohs(se1->s_port); + } else { + struct scache *s; + for (s = rlist; s; s = s->next) { + if ((s->proto == UDP_PROTO && + (current_filter.dbs&(1<<UDP_DB))) || + (s->proto == TCP_PROTO && + (current_filter.dbs&(1<<TCP_DB)))) { + if (s->name && strcmp(s->name, port) == 0) { + if (a.port > 0 && a.port != s->port) { + fprintf(stderr, "Error: ambiguous port \"%s\".\n", port); + return NULL; + } + a.port = s->port; + } + } + } + if (a.port <= 0) { + fprintf(stderr, "Error: \"%s\" does not look like a port.\n", port); + return NULL; + } + } + } + } + } + if (addr && *addr && *addr != '*') { + if (get_prefix_1(&a.addr, addr, fam)) { + if (get_dns_host(&a, addr, fam)) { + fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", addr); + return NULL; + } + } + } + + out: + res = malloc(sizeof(*res)); + if (res) + memcpy(res, &a, sizeof(a)); + return res; +} + +static int tcp_show_line(char *line, struct filter *f, int family) +{ + struct tcpstat s; + char *loc, *rem, *data; + char opt[256]; + int n; + char *p; + + if ((p = strchr(line, ':')) == NULL) + return -1; + loc = p+2; + + if ((p = strchr(loc, ':')) == NULL) + return -1; + p[5] = 0; + rem = p+6; + + if ((p = strchr(rem, ':')) == NULL) + return -1; + p[5] = 0; + data = p+6; + + do { + int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + + if (!(f->states & (1<<state))) + return 0; + } while (0); + + s.local.family = s.remote.family = family; + if (family == AF_INET) { + sscanf(loc, "%x:%x", s.local.data, (unsigned*)&s.lport); + sscanf(rem, "%x:%x", s.remote.data, (unsigned*)&s.rport); + s.local.bytelen = s.remote.bytelen = 4; + } else { + sscanf(loc, "%08x%08x%08x%08x:%x", + s.local.data, + s.local.data+1, + s.local.data+2, + s.local.data+3, + &s.lport); + sscanf(rem, "%08x%08x%08x%08x:%x", + s.remote.data, + s.remote.data+1, + s.remote.data+2, + s.remote.data+3, + &s.rport); + s.local.bytelen = s.remote.bytelen = 16; + } + + if (f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + opt[0] = 0; + n = sscanf(data, "%x %x:%x %x:%x %x %d %d %d %d %llx %d %d %d %d %d %[^\n]\n", + &s.state, &s.wq, &s.rq, + &s.timer, &s.timeout, &s.retrs, &s.uid, &s.probes, &s.ino, + &s.refcnt, &s.sk, &s.rto, &s.ato, &s.qack, + &s.cwnd, &s.ssthresh, opt); + + if (n < 17) + opt[0] = 0; + + if (n < 12) { + s.rto = 0; + s.cwnd = 2; + s.ssthresh = -1; + s.ato = s.qack = 0; + } + + if (netid_width) + printf("%-*s ", netid_width, "tcp"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", s.rq, s.wq); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_options) { + if (s.timer) { + if (s.timer > 4) + s.timer = 5; + printf(" timer:(%s,%s,%d)", + tmr_name[s.timer], + print_hz_timer(s.timeout), + s.timer != 1 ? s.probes : s.retrs); + } + } + if (show_tcpinfo) { + if (s.rto && s.rto != 3*get_hz()) + printf(" rto:%g", (double)s.rto/get_hz()); + if (s.ato) + printf(" ato:%g", (double)s.ato/get_hz()); + if (s.cwnd != 2) + printf(" cwnd:%d", s.cwnd); + if (s.ssthresh != -1) + printf(" ssthresh:%d", s.ssthresh); + if (s.qack/2) + printf(" qack:%d", s.qack/2); + if (s.qack&1) + printf(" bidir"); + } + if (show_users) { + char ubuf[4096]; + if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + if (s.uid) + printf(" uid:%u", (unsigned)s.uid); + printf(" ino:%u", (unsigned)s.ino); + printf(" sk:%llx", s.sk); + if (opt[0]) + printf(" opt:\"%s\"", opt); + } + printf("\n"); + + return 0; +} + +static int generic_record_read(int fd, char *buf, int bufsize, + int (*worker)(char*, struct filter *, int), + struct filter *f, int fam) +{ + int n; + int recsize; + int eof = 0; + char *p; + + /* Load the first chunk and calculate record length from it. */ + n = read(fd, buf, bufsize); + if (n < 0) + goto outerr; + /* I _know_ that this is wrong, do not remind. :-) + * But this works nowadays. */ + if (n < bufsize) + eof = 1; + p = memchr(buf, '\n', n); + if (p == NULL || (p-buf) >= n) + goto outwrongformat; + recsize = (p-buf)+1; + p = buf+recsize; + + for (;;) { + while ((p+recsize) - buf <= n) { + if (p[recsize-1] != '\n') + goto outwrongformat; + p[recsize-1] = 0; + if (worker(p, f, fam) < 0) + goto done; + p += recsize; + } + if (!eof) { + int remains = (buf+bufsize) - p; + memcpy(buf, p, remains); + p = buf+remains; + n = read(fd, p, (buf+bufsize) - p); + if (n < 0) + goto outerr; + if (n < (buf+bufsize) - p) { + eof = 1; + if (n == 0) { + if (remains) + goto outwrongformat; + goto done; + } + } + n += remains; + p = buf; + } else { + if (p != buf+n) + goto outwrongformat; + goto done; + } + } +done: + return 0; + +outwrongformat: + errno = EINVAL; +outerr: + return -1; +} + +static char *sprint_bw(char *buf, double bw) +{ + if (bw > 1000000.) + sprintf(buf,"%.1fM", bw / 1000000.); + else if (bw > 1000.) + sprintf(buf,"%.1fK", bw / 1000.); + else + sprintf(buf, "%g", bw); + + return buf; +} + +static void tcp_show_info(const struct nlmsghdr *nlh, struct tcpdiagmsg *r) +{ + struct rtattr * tb[TCPDIAG_MAX+1]; + char b1[64]; + double rtt = 0; + + parse_rtattr(tb, TCPDIAG_MAX, (struct rtattr*)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[TCPDIAG_MEMINFO]) { + const struct tcpdiag_meminfo *minfo + = RTA_DATA(tb[TCPDIAG_MEMINFO]); + printf(" mem:(r%u,w%u,f%u,t%u)", + minfo->tcpdiag_rmem, + minfo->tcpdiag_wmem, + minfo->tcpdiag_fmem, + minfo->tcpdiag_tmem); + } + + if (tb[TCPDIAG_INFO]) { + struct tcp_info *info; + int len = RTA_PAYLOAD(tb[TCPDIAG_INFO]); + + /* workaround for older kernels with less fields */ + if (len < sizeof(*info)) { + info = alloca(sizeof(*info)); + memset(info, 0, sizeof(*info)); + memcpy(info, RTA_DATA(tb[TCPDIAG_INFO]), len); + } else + info = RTA_DATA(tb[TCPDIAG_INFO]); + + if (show_options) { + if (info->tcpi_options & TCPI_OPT_TIMESTAMPS) + printf(" ts"); + if (info->tcpi_options & TCPI_OPT_SACK) + printf(" sack"); + if (info->tcpi_options & TCPI_OPT_ECN) + printf(" ecn"); + } + if (info->tcpi_options & TCPI_OPT_WSCALE) + printf(" wscale:%d,%d", info->tcpi_snd_wscale, + info->tcpi_rcv_wscale); + if (info->tcpi_rto && info->tcpi_rto != 3000000) + printf(" rto:%g", (double)info->tcpi_rto/1000); + if (info->tcpi_rtt) + printf(" rtt:%g/%g", (double)info->tcpi_rtt/1000, + (double)info->tcpi_rttvar/1000); + if (info->tcpi_ato) + printf(" ato:%g", (double)info->tcpi_ato/1000); + if (info->tcpi_snd_cwnd != 2) + printf(" cwnd:%d", info->tcpi_snd_cwnd); + if (info->tcpi_snd_ssthresh < 0xFFFF) + printf(" ssthresh:%d", info->tcpi_snd_ssthresh); + + rtt = (double) info->tcpi_rtt; + if (tb[TCPDIAG_VEGASINFO]) { + const struct tcpvegas_info *vinfo + = RTA_DATA(tb[TCPDIAG_VEGASINFO]); + + if (vinfo->tcpv_enabled) + printf(" vegas"); + + if (vinfo->tcpv_rtt && + vinfo->tcpv_rtt != 0x7fffffff) + rtt = vinfo->tcpv_rtt; + } + + if (rtt > 0 && info->tcpi_snd_mss && info->tcpi_snd_cwnd) { + printf(" send %sbps", + sprint_bw(b1, (double) info->tcpi_snd_cwnd * + (double) info->tcpi_snd_mss * 8000000. + / rtt)); + } + + if (info->tcpi_rcv_rtt) + printf(" rcv_rtt:%g", (double) info->tcpi_rcv_rtt/1000); + if (info->tcpi_rcv_space) + printf(" rcv_space:%d", info->tcpi_rcv_space); + + } +} + +int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f) +{ + struct tcpdiagmsg *r = NLMSG_DATA(nlh); + struct tcpstat s; + + s.state = r->tcpdiag_state; + s.local.family = s.remote.family = r->tcpdiag_family; + s.lport = ntohs(r->id.tcpdiag_sport); + s.rport = ntohs(r->id.tcpdiag_dport); + if (s.local.family == AF_INET) { + s.local.bytelen = s.remote.bytelen = 4; + } else { + s.local.bytelen = s.remote.bytelen = 16; + } + memcpy(s.local.data, r->id.tcpdiag_src, s.local.bytelen); + memcpy(s.remote.data, r->id.tcpdiag_dst, s.local.bytelen); + + if (f && f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + if (netid_width) + printf("%-*s ", netid_width, "tcp"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", r->tcpdiag_rqueue, r->tcpdiag_wqueue); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_options) { + if (r->tcpdiag_timer) { + if (r->tcpdiag_timer > 4) + r->tcpdiag_timer = 5; + printf(" timer:(%s,%s,%d)", + tmr_name[r->tcpdiag_timer], + print_ms_timer(r->tcpdiag_expires), + r->tcpdiag_retrans); + } + } + if (show_users) { + char ubuf[4096]; + if (find_users(r->tcpdiag_inode, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + if (r->tcpdiag_uid) + printf(" uid:%u", (unsigned)r->tcpdiag_uid); + printf(" ino:%u", (unsigned)r->tcpdiag_inode); + printf(" sk:%08x", r->id.tcpdiag_cookie[0]); + if (r->id.tcpdiag_cookie[1] != 0) + printf("%08x", r->id.tcpdiag_cookie[1]); + } + if (show_mem || show_tcpinfo) { + printf("\n\t"); + tcp_show_info(nlh, r); + } + + printf("\n"); + + return 0; + +} + +int tcp_show_netlink(struct filter *f, FILE *dump_fp) +{ + int fd; + struct sockaddr_nl nladdr; + struct { + struct nlmsghdr nlh; + struct tcpdiagreq r; + } req; + char *bc = NULL; + int bclen; + struct msghdr msg; + struct rtattr rta; + char buf[8192]; + struct iovec iov[3]; + + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TCPDIAG)) < 0) + return -1; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = TCPDIAG_GETSOCK; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 123456; + memset(&req.r, 0, sizeof(req.r)); + req.r.tcpdiag_family = AF_INET; + req.r.tcpdiag_states = f->states; + if (show_mem) + req.r.tcpdiag_ext |= (1<<(TCPDIAG_MEMINFO-1)); + + if (show_tcpinfo) { + req.r.tcpdiag_ext |= (1<<(TCPDIAG_INFO-1)); + req.r.tcpdiag_ext |= (1<<(TCPDIAG_VEGASINFO-1)); + } + + iov[0] = (struct iovec){ &req, sizeof(req) }; + if (f->f) { + bclen = ssfilter_bytecompile(f->f, &bc); + rta.rta_type = TCPDIAG_REQ_BYTECODE; + rta.rta_len = RTA_LENGTH(bclen); + iov[1] = (struct iovec){ &rta, sizeof(rta) }; + iov[2] = (struct iovec){ bc, bclen }; + req.nlh.nlmsg_len += RTA_LENGTH(bclen); + } + + msg = (struct msghdr) { + (void*)&nladdr, sizeof(nladdr), + iov, f->f ? 3 : 1, + NULL, 0, + 0 + }; + + if (sendmsg(fd, &msg, 0) < 0) + return -1; + + + iov[0] = (struct iovec){ buf, sizeof(buf) }; + + while (1) { + int status; + struct nlmsghdr *h; + + msg = (struct msghdr) { + (void*)&nladdr, sizeof(nladdr), + iov, 1, + NULL, 0, + 0 + }; + + status = recvmsg(fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return 0; + } + + if (dump_fp) + fwrite(buf, 1, NLMSG_ALIGN(status), dump_fp); + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (/*h->nlmsg_pid != rth->local.nl_pid ||*/ + h->nlmsg_seq != 123456) + goto skip_it; + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("TCPDIAG answers"); + } + return 0; + } + if (!dump_fp) { + err = tcp_show_sock(h, NULL); + if (err < 0) + return err; + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } + return 0; +} + +int tcp_show_netlink_file(struct filter *f) +{ + FILE *fp; + char buf[8192]; + + if ((fp = fopen(getenv("TCPDIAG_FILE"), "r")) == NULL) { + perror("fopen($TCPDIAG_FILE)"); + return -1; + } + + while (1) { + int status, err; + struct nlmsghdr *h = (struct nlmsghdr*)buf; + + status = fread(buf, 1, sizeof(*h), fp); + if (status < 0) { + perror("Reading header from $TCPDIAG_FILE"); + return -1; + } + if (status != sizeof(*h)) { + perror("Unexpected EOF reading $TCPDIAG_FILE"); + return -1; + } + + status = fread(h+1, 1, NLMSG_ALIGN(h->nlmsg_len-sizeof(*h)), fp); + + if (status < 0) { + perror("Reading $TCPDIAG_FILE"); + return -1; + } + if (status + sizeof(*h) < h->nlmsg_len) { + perror("Unexpected EOF reading $TCPDIAG_FILE"); + return -1; + } + + /* The only legal exit point */ + if (h->nlmsg_type == NLMSG_DONE) + return 0; + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("TCPDIAG answered"); + } + return -1; + } + + err = tcp_show_sock(h, f); + if (err < 0) + return err; + } +} + +int tcp_show(struct filter *f) +{ + int fd = -1; + char *buf = NULL; + int bufsize = 64*1024; + + dg_proto = TCP_PROTO; + + if (getenv("TCPDIAG_FILE")) + return tcp_show_netlink_file(f); + + if (!getenv("PROC_NET_TCP") && !getenv("PROC_ROOT") + && tcp_show_netlink(f, NULL) == 0) + return 0; + + /* Sigh... We have to parse /proc/net/tcp... */ + + /* Estimate amount of sockets and try to allocate + * huge buffer to read all the table at one read. + * Limit it by 16MB though. The assumption is: as soon as + * kernel was able to hold information about N connections, + * it is able to give us some memory for snapshot. + */ + if (1) { + int guess = slabstat.socks+slabstat.tcp_syns; + if (f->states&(1<<SS_TIME_WAIT)) + guess += slabstat.tcp_tws; + if (guess > (16*1024*1024)/128) + guess = (16*1024*1024)/128; + guess *= 128; + if (guess > bufsize) + bufsize = guess; + } + while (bufsize >= 64*1024) { + if ((buf = malloc(bufsize)) != NULL) + break; + bufsize /= 2; + } + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + + if (f->families & (1<<AF_INET)) { + if ((fd = net_tcp_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families & (1<<AF_INET6)) && + (fd = net_tcp6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, tcp_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + + free(buf); + return 0; + +outerr: + do { + int saved_errno = errno; + if (buf) + free(buf); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + + +int dgram_show_line(char *line, struct filter *f, int family) +{ + struct tcpstat s; + char *loc, *rem, *data; + char opt[256]; + int n; + char *p; + + if ((p = strchr(line, ':')) == NULL) + return -1; + loc = p+2; + + if ((p = strchr(loc, ':')) == NULL) + return -1; + p[5] = 0; + rem = p+6; + + if ((p = strchr(rem, ':')) == NULL) + return -1; + p[5] = 0; + data = p+6; + + do { + int state = (data[1] >= 'A') ? (data[1] - 'A' + 10) : (data[1] - '0'); + + if (!(f->states & (1<<state))) + return 0; + } while (0); + + s.local.family = s.remote.family = family; + if (family == AF_INET) { + sscanf(loc, "%x:%x", s.local.data, (unsigned*)&s.lport); + sscanf(rem, "%x:%x", s.remote.data, (unsigned*)&s.rport); + s.local.bytelen = s.remote.bytelen = 4; + } else { + sscanf(loc, "%08x%08x%08x%08x:%x", + s.local.data, + s.local.data+1, + s.local.data+2, + s.local.data+3, + &s.lport); + sscanf(rem, "%08x%08x%08x%08x:%x", + s.remote.data, + s.remote.data+1, + s.remote.data+2, + s.remote.data+3, + &s.rport); + s.local.bytelen = s.remote.bytelen = 16; + } + + if (f->f && run_ssfilter(f->f, &s) == 0) + return 0; + + opt[0] = 0; + n = sscanf(data, "%x %x:%x %*x:%*x %*x %d %*d %d %d %llx %[^\n]\n", + &s.state, &s.wq, &s.rq, + &s.uid, &s.ino, + &s.refcnt, &s.sk, opt); + + if (n < 9) + opt[0] = 0; + + if (netid_width) + printf("%-*s ", netid_width, dg_proto); + if (state_width) + printf("%-*s ", state_width, sstate_name[s.state]); + + printf("%-6d %-6d ", s.rq, s.wq); + + formatted_print(&s.local, s.lport); + formatted_print(&s.remote, s.rport); + + if (show_users) { + char ubuf[4096]; + if (find_users(s.ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + + if (show_details) { + if (s.uid) + printf(" uid=%u", (unsigned)s.uid); + printf(" ino=%u", (unsigned)s.ino); + printf(" sk=%llx", s.sk); + if (opt[0]) + printf(" opt:\"%s\"", opt); + } + printf("\n"); + + return 0; +} + + +int udp_show(struct filter *f) +{ + int fd = -1; + char buf[8192]; + int bufsize = sizeof(buf); + + dg_proto = UDP_PROTO; + + if (f->families&(1<<AF_INET)) { + if ((fd = net_udp_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families&(1<<AF_INET6)) && + (fd = net_udp6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + return 0; + +outerr: + do { + int saved_errno = errno; + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + +int raw_show(struct filter *f) +{ + int fd = -1; + char buf[8192]; + int bufsize = sizeof(buf); + + dg_proto = RAW_PROTO; + + if (f->families&(1<<AF_INET)) { + if ((fd = net_raw_open()) < 0) + goto outerr; + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET)) + goto outerr; + close(fd); + } + + if ((f->families&(1<<AF_INET6)) && + (fd = net_raw6_open()) >= 0) { + if (generic_record_read(fd, buf, bufsize, dgram_show_line, f, AF_INET6)) + goto outerr; + close(fd); + } + return 0; + +outerr: + do { + int saved_errno = errno; + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; + } while (0); +} + + +struct unixstat +{ + struct unixstat *next; + int ino; + int peer; + int rq; + int wq; + int state; + int type; + char *name; +}; + + + +int unix_state_map[] = { SS_CLOSE, SS_SYN_SENT, + SS_ESTABLISHED, SS_CLOSING }; + + +#define MAX_UNIX_REMEMBER (1024*1024/sizeof(struct unixstat)) + +void unix_list_free(struct unixstat *list) +{ + while (list) { + struct unixstat *s = list; + list = list->next; + if (s->name) + free(s->name); + free(s); + } +} + +void unix_list_print(struct unixstat *list, struct filter *f) +{ + struct unixstat *s; + char *peer; + + for (s = list; s; s = s->next) { + if (!(f->states & (1<<s->state))) + continue; + if (s->type == SOCK_STREAM && !(f->dbs&(1<<UNIX_ST_DB))) + continue; + if (s->type == SOCK_DGRAM && !(f->dbs&(1<<UNIX_DG_DB))) + continue; + + peer = "*"; + if (s->peer) { + struct unixstat *p; + for (p = list; p; p = p->next) { + if (s->peer == p->ino) + break; + } + if (!p) { + peer = "?"; + } else { + peer = p->name ? : "*"; + } + } + + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_UNIX; + tst.remote.family = AF_UNIX; + memcpy(tst.local.data, &s->name, sizeof(s->name)); + if (strcmp(peer, "*") == 0) + memset(tst.remote.data, 0, sizeof(peer)); + else + memcpy(tst.remote.data, &peer, sizeof(peer)); + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, + s->type == SOCK_STREAM ? "u_str" : "u_dgr"); + if (state_width) + printf("%-*s ", state_width, sstate_name[s->state]); + printf("%-6d %-6d ", s->rq, s->wq); + printf("%*s %-*d %*s %-*d", + addr_width, s->name ? : "*", serv_width, s->ino, + addr_width, peer, serv_width, s->peer); + if (show_users) { + char ubuf[4096]; + if (find_users(s->ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + printf("\n"); + } +} + +int unix_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + char name[128]; + int newformat = 0; + int cnt; + struct unixstat *list = NULL; + + if ((fp = fdopen(net_unix_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + if (memcmp(buf, "Peer", 4) == 0) + newformat = 1; + cnt = 0; + + while (fgets(buf, sizeof(buf)-1, fp)) { + struct unixstat *u, **insp; + int flags; + + if (!(u = malloc(sizeof(*u)))) + break; + u->name = NULL; + + if (sscanf(buf, "%x: %x %x %x %x %x %d %s", + &u->peer, &u->rq, &u->wq, &flags, &u->type, + &u->state, &u->ino, name) < 8) + name[0] = 0; + + if (flags&(1<<16)) { + u->state = SS_LISTEN; + } else { + u->state = unix_state_map[u->state-1]; + if (u->type == SOCK_DGRAM && + u->state == SS_CLOSE && + u->peer) + u->state = SS_ESTABLISHED; + } + + if (!newformat) { + u->peer = 0; + u->rq = 0; + u->wq = 0; + } + + insp = &list; + while (*insp) { + if (u->type < (*insp)->type || + (u->type == (*insp)->type && + u->ino < (*insp)->ino)) + break; + insp = &(*insp)->next; + } + u->next = *insp; + *insp = u; + + if (name[0]) { + if ((u->name = malloc(strlen(name)+1)) == NULL) + break; + strcpy(u->name, name); + } + if (++cnt > MAX_UNIX_REMEMBER) { + unix_list_print(list, f); + unix_list_free(list); + list = NULL; + cnt = 0; + } + } + + if (list) { + unix_list_print(list, f); + unix_list_free(list); + list = NULL; + cnt = 0; + } + + return 0; +} + + +int packet_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + int type; + int prot; + int iface; + int state; + int rq; + int uid; + int ino; + unsigned long long sk; + + if (!(f->states & (1<<SS_CLOSE))) + return 0; + + if ((fp = fdopen(net_packet_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + while (fgets(buf, sizeof(buf)-1, fp)) { + sscanf(buf, "%llx %*d %d %x %d %d %u %u %u", + &sk, + &type, &prot, &iface, &state, + &rq, &uid, &ino); + + if (type == SOCK_RAW && !(f->dbs&(1<<PACKET_R_DB))) + continue; + if (type == SOCK_DGRAM && !(f->dbs&(1<<PACKET_DG_DB))) + continue; + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_PACKET; + tst.remote.family = AF_PACKET; + tst.rport = 0; + tst.lport = iface; + tst.local.data[0] = prot; + tst.remote.data[0] = 0; + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, + type == SOCK_RAW ? "p_raw" : "p_dgr"); + if (state_width) + printf("%-*s ", state_width, "UNCONN"); + printf("%-6d %-6d ", rq, 0); + if (prot == 3) { + printf("%*s:", addr_width, "*"); + } else { + char tb[16]; + printf("%*s:", addr_width, + ll_proto_n2a(htons(prot), tb, sizeof(tb))); + } + if (iface == 0) { + printf("%-*s ", serv_width, "*"); + } else { + printf("%-*s ", serv_width, xll_index_to_name(iface)); + } + printf("%*s*%-*s", + addr_width, "", serv_width, ""); + + if (show_users) { + char ubuf[4096]; + if (find_users(ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + printf(" ino=%u uid=%u sk=%llx", ino, uid, sk); + } + printf("\n"); + } + + return 0; +} + +int netlink_show(struct filter *f) +{ + FILE *fp; + char buf[256]; + int prot, pid; + unsigned groups; + int rq, wq, rc; + unsigned long long sk, cb; + + if (!(f->states & (1<<SS_CLOSE))) + return 0; + + if ((fp = fdopen(net_netlink_open(), "r")) == NULL) + return -1; + fgets(buf, sizeof(buf)-1, fp); + + while (fgets(buf, sizeof(buf)-1, fp)) { + sscanf(buf, "%llx %d %d %x %d %d %llx %d", + &sk, + &prot, &pid, &groups, &rq, &wq, &cb, &rc); + + if (f->f) { + struct tcpstat tst; + tst.local.family = AF_NETLINK; + tst.remote.family = AF_NETLINK; + tst.rport = -1; + tst.lport = pid; + tst.local.data[0] = prot; + tst.remote.data[0] = 0; + if (run_ssfilter(f->f, &tst) == 0) + continue; + } + + if (netid_width) + printf("%-*s ", netid_width, "nl"); + if (state_width) + printf("%-*s ", state_width, "UNCONN"); + printf("%-6d %-6d ", rq, wq); + if (resolve_services && prot == 0) + printf("%*s:", addr_width, "rtnl"); + else if (resolve_services && prot == 3) + printf("%*s:", addr_width, "fw"); + else if (resolve_services && prot == 4) + printf("%*s:", addr_width, "tcpdiag"); + else + printf("%*d:", addr_width, prot); + if (pid == -1) { + printf("%-*s ", serv_width, "*"); + } else if (resolve_services) { + int done = 0; + if (!pid) { + done = 1; + printf("%-*s ", serv_width, "kernel"); + } else if (pid > 0) { + char procname[64]; + FILE *fp; + sprintf(procname, "%s/%d/stat", + getenv("PROC_ROOT") ? : "/proc", pid); + if ((fp = fopen(procname, "r")) != NULL) { + if (fscanf(fp, "%*d (%[^)])", procname) == 1) { + sprintf(procname+strlen(procname), "/%d", pid); + printf("%-*s ", serv_width, procname); + done = 1; + } + fclose(fp); + } + } + if (!done) + printf("%-*d ", serv_width, pid); + } else { + printf("%-*d ", serv_width, pid); + } + printf("%*s*%-*s", + addr_width, "", serv_width, ""); + + if (show_details) { + printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups); + } + printf("\n"); + } + + return 0; +} + +struct snmpstat +{ + int tcp_estab; +}; + +int get_snmp_int(char *proto, char *key, int *result) +{ + char buf[1024]; + FILE *fp; + int protolen = strlen(proto); + int keylen = strlen(key); + + *result = 0; + + if ((fp = fdopen(net_snmp_open(), "r")) == NULL) + return -1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *p = buf; + int pos = 0; + if (memcmp(buf, proto, protolen)) + continue; + while ((p = strchr(p, ' ')) != NULL) { + pos++; + p++; + if (memcmp(p, key, keylen) == 0 && + (p[keylen] == ' ' || p[keylen] == '\n')) + break; + } + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + if (memcmp(buf, proto, protolen)) + break; + p = buf; + while ((p = strchr(p, ' ')) != NULL) { + p++; + if (--pos == 0) { + sscanf(p, "%d", result); + fclose(fp); + return 0; + } + } + } + + fclose(fp); + errno = ESRCH; + return -1; +} + + +/* Get stats from sockstat */ + +struct sockstat +{ + int socks; + int tcp_mem; + int tcp_total; + int tcp_orphans; + int tcp_tws; + int tcp4_hashed; + int udp4; + int raw4; + int frag4; + int frag4_mem; + int tcp6_hashed; + int udp6; + int raw6; + int frag6; + int frag6_mem; +}; + +static void get_sockstat_line(char *line, struct sockstat *s) +{ + char id[256], rem[256]; + + if (sscanf(line, "%[^ ] %[^\n]\n", id, rem) != 2) + return; + + if (strcmp(id, "sockets:") == 0) + sscanf(rem, "%*s%d", &s->socks); + else if (strcmp(id, "UDP:") == 0) + sscanf(rem, "%*s%d", &s->udp4); + else if (strcmp(id, "UDP6:") == 0) + sscanf(rem, "%*s%d", &s->udp6); + else if (strcmp(id, "RAW:") == 0) + sscanf(rem, "%*s%d", &s->raw4); + else if (strcmp(id, "RAW6:") == 0) + sscanf(rem, "%*s%d", &s->raw6); + else if (strcmp(id, "TCP6:") == 0) + sscanf(rem, "%*s%d", &s->tcp6_hashed); + else if (strcmp(id, "FRAG:") == 0) + sscanf(rem, "%*s%d%*s%d", &s->frag4, &s->frag4_mem); + else if (strcmp(id, "FRAG6:") == 0) + sscanf(rem, "%*s%d%*s%d", &s->frag6, &s->frag6_mem); + else if (strcmp(id, "TCP:") == 0) + sscanf(rem, "%*s%d%*s%d%*s%d%*s%d%*s%d", + &s->tcp4_hashed, + &s->tcp_orphans, &s->tcp_tws, &s->tcp_total, &s->tcp_mem); +} + +int get_sockstat(struct sockstat *s) +{ + char buf[256]; + FILE *fp; + + memset(s, 0, sizeof(*s)); + + if ((fp = fdopen(net_sockstat_open(), "r")) == NULL) + return -1; + while(fgets(buf, sizeof(buf), fp) != NULL) + get_sockstat_line(buf, s); + fclose(fp); + + if ((fp = fdopen(net_sockstat6_open(), "r")) == NULL) + return 0; + while(fgets(buf, sizeof(buf), fp) != NULL) + get_sockstat_line(buf, s); + fclose(fp); + + return 0; +} + +int print_summary(void) +{ + struct sockstat s; + struct snmpstat sn; + + if (get_sockstat(&s) < 0) + perror("ss: get_sockstat"); + if (get_snmp_int("Tcp:", "CurrEstab", &sn.tcp_estab) < 0) + perror("ss: get_snmpstat"); + + printf("Total: %d (kernel %d)\n", s.socks, slabstat.socks); + + printf("TCP: %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n", + s.tcp_total + slabstat.tcp_syns + s.tcp_tws, + sn.tcp_estab, + s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws), + s.tcp_orphans, + slabstat.tcp_syns, + s.tcp_tws, slabstat.tcp_tws, + slabstat.tcp_ports + ); + + printf("\n"); + printf("Transport Total IP IPv6\n"); + printf("* %-9d %-9s %-9s\n", slabstat.socks, "-", "-"); + printf("RAW %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6); + printf("UDP %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6); + printf("TCP %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, s.tcp4_hashed, s.tcp6_hashed); + printf("INET %-9d %-9d %-9d\n", + s.raw4+s.udp4+s.tcp4_hashed+ + s.raw6+s.udp6+s.tcp6_hashed, + s.raw4+s.udp4+s.tcp4_hashed, + s.raw6+s.udp6+s.tcp6_hashed); + printf("FRAG %-9d %-9d %-9d\n", s.frag4+s.frag6, s.frag4, s.frag6); + + printf("\n"); + + return 0; +} + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, +"Usage: ss [ OPTIONS ]\n" +" ss [ OPTIONS ] [ FILTER ]\n" +" -h, --help this message\n" +" -V, --version output version information\n" +" -n, --numeric don't resolve service names\n" +" -r, --resolve resolve host names\n" +" -a, --all display all sockets\n" +" -l, --listening display listening sockets\n" +" -o, --options show timer information\n" +" -e, --extended show detailed socket information\n" +" -m, --memory show socket memory usage\n" +" -p, --processes show process using socket\n" +" -i, --info show internal TCP information\n" +" -s, --summary show socket usage summary\n" +"\n" +" -4, --ipv4 display only IP version 4 sockets\n" +" -6, --ipv6 display only IP version 6 sockets\n" +" -0, --packet display PACKET sockets\n" +" -t, --tcp display only TCP sockets\n" +" -u, --udp display only UDP sockets\n" +" -w, --raw display only RAW sockets\n" +" -x, --unix display only Unix domain sockets\n" +" -f, --family=FAMILY display sockets of type FAMILY\n" +"\n" +" -A, --query=QUERY\n" +" QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]\n" +"\n" +" -F, --filter=FILE read filter information from FILE\n" +" FILTER := [ state TCP-STATE ] [ EXPRESSION ]\n" + ); + exit(-1); +} + + +int scan_state(const char *state) +{ + int i; + if (strcasecmp(state, "close") == 0 || + strcasecmp(state, "closed") == 0) + return (1<<SS_CLOSE); + if (strcasecmp(state, "syn-rcv") == 0) + return (1<<SS_SYN_RECV); + if (strcasecmp(state, "established") == 0) + return (1<<SS_ESTABLISHED); + if (strcasecmp(state, "all") == 0) + return SS_ALL; + if (strcasecmp(state, "connected") == 0) + return SS_ALL & ~((1<<SS_CLOSE)|(1<<SS_LISTEN)); + if (strcasecmp(state, "synchronized") == 0) + return SS_ALL & ~((1<<SS_CLOSE)|(1<<SS_LISTEN)|(1<<SS_SYN_SENT)); + if (strcasecmp(state, "bucket") == 0) + return (1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT); + if (strcasecmp(state, "big") == 0) + return SS_ALL & ~((1<<SS_SYN_RECV)|(1<<SS_TIME_WAIT)); + for (i=0; i<SS_MAX; i++) { + if (strcasecmp(state, sstate_namel[i]) == 0) + return (1<<i); + } + return 0; +} + +static const struct option long_opts[] = { + { "numeric", 0, 0, 'n' }, + { "resolve", 0, 0, 'r' }, + { "options", 0, 0, 'o' }, + { "extended", 0, 0, 'e' }, + { "memory", 0, 0, 'm' }, + { "info", 0, 0, 'i' }, + { "processes", 0, 0, 'p' }, + { "tcp", 0, 0, 't' }, + { "udp", 0, 0, 'u' }, + { "raw", 0, 0, 'w' }, + { "unix", 0, 0, 'x' }, + { "all", 0, 0, 'a' }, + { "listening", 0, 0, 'l' }, + { "ipv4", 0, 0, '4' }, + { "ipv6", 0, 0, '6' }, + { "packet", 0, 0, '0' }, + { "family", 1, 0, 'f' }, + { "socket", 1, 0, 'A' }, + { "summary", 0, 0, 's' }, + { "diag", 0, 0, 'D' }, + { "filter", 1, 0, 'F' }, + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { 0 } + +}; + +int main(int argc, char *argv[]) +{ + int do_default = 1; + int saw_states = 0; + int saw_query = 0; + int do_summary = 0; + const char *dump_tcpdiag = NULL; + FILE *filter_fp = NULL; + int ch; + + memset(¤t_filter, 0, sizeof(current_filter)); + + current_filter.states = default_filter.states; + + while ((ch = getopt_long(argc, argv, "haletuwxnro460spf:miA:D:F:vV", + long_opts, NULL)) != EOF) { + switch(ch) { + case 'n': + resolve_services = 0; + break; + case 'r': + resolve_hosts = 1; + break; + case 'o': + show_options = 1; + break; + case 'e': + show_options = 1; + show_details++; + break; + case 'm': + show_mem = 1; + break; + case 'i': + show_tcpinfo = 1; + break; + case 'p': + show_users++; + break; + case 't': + current_filter.dbs |= (1<<TCP_DB); + do_default = 0; + break; + case 'u': + current_filter.dbs |= (1<<UDP_DB); + do_default = 0; + break; + case 'w': + current_filter.dbs |= (1<<RAW_DB); + do_default = 0; + break; + case 'x': + current_filter.dbs |= UNIX_DBM; + do_default = 0; + break; + case 'a': + current_filter.states = SS_ALL; + break; + case 'l': + current_filter.states = (1<<SS_LISTEN); + break; + case '4': + preferred_family = AF_INET; + break; + case '6': + preferred_family = AF_INET6; + break; + case '0': + preferred_family = AF_PACKET; + break; + case 'f': + if (strcmp(optarg, "inet") == 0) + preferred_family = AF_INET; + else if (strcmp(optarg, "inet6") == 0) + preferred_family = AF_INET6; + else if (strcmp(optarg, "link") == 0) + preferred_family = AF_PACKET; + else if (strcmp(optarg, "unix") == 0) + preferred_family = AF_UNIX; + else if (strcmp(optarg, "netlink") == 0) + preferred_family = AF_NETLINK; + else if (strcmp(optarg, "help") == 0) + usage(); + else { + fprintf(stderr, "ss: \"%s\" is invalid family\n", optarg); + usage(); + } + break; + case 'A': + { + char *p, *p1; + if (!saw_query) { + current_filter.dbs = 0; + saw_query = 1; + do_default = 0; + } + p = p1 = optarg; + do { + if ((p1 = strchr(p, ',')) != NULL) + *p1 = 0; + if (strcmp(p, "all") == 0) { + current_filter.dbs = ALL_DB; + } else if (strcmp(p, "inet") == 0) { + current_filter.dbs |= (1<<TCP_DB)|(1<<UDP_DB)|(1<<RAW_DB); + } else if (strcmp(p, "udp") == 0) { + current_filter.dbs |= (1<<UDP_DB); + } else if (strcmp(p, "tcp") == 0) { + current_filter.dbs |= (1<<TCP_DB); + } else if (strcmp(p, "raw") == 0) { + current_filter.dbs |= (1<<RAW_DB); + } else if (strcmp(p, "unix") == 0) { + current_filter.dbs |= UNIX_DBM; + } else if (strcasecmp(p, "unix_stream") == 0 || + strcmp(p, "u_str") == 0) { + current_filter.dbs |= (1<<UNIX_ST_DB); + } else if (strcasecmp(p, "unix_dgram") == 0 || + strcmp(p, "u_dgr") == 0) { + current_filter.dbs |= (1<<UNIX_DG_DB); + } else if (strcmp(p, "packet") == 0) { + current_filter.dbs |= PACKET_DBM; + } else if (strcmp(p, "packet_raw") == 0 || + strcmp(p, "p_raw") == 0) { + current_filter.dbs |= (1<<PACKET_R_DB); + } else if (strcmp(p, "packet_dgram") == 0 || + strcmp(p, "p_dgr") == 0) { + current_filter.dbs |= (1<<PACKET_DG_DB); + } else if (strcmp(p, "netlink") == 0) { + current_filter.dbs |= (1<<NETLINK_DB); + } else { + fprintf(stderr, "ss: \"%s\" is illegal socket table id\n", p); + usage(); + } + p = p1 + 1; + } while (p1); + break; + } + case 's': + do_summary = 1; + break; + case 'D': + dump_tcpdiag = optarg; + break; + case 'F': + if (filter_fp) { + fprintf(stderr, "More than one filter file\n"); + exit(-1); + } + if (optarg[0] == '-') + filter_fp = stdin; + else + filter_fp = fopen(optarg, "r"); + if (!filter_fp) { + perror("fopen filter file"); + exit(-1); + } + break; + case 'v': + case 'V': + printf("ss utility, iproute2-ss%s\n", SNAPSHOT); + exit(0); + case 'h': + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + get_slabstat(&slabstat); + + if (do_summary) { + print_summary(); + if (do_default && argc == 0) + exit(0); + } + + if (do_default) + current_filter.dbs = default_filter.dbs; + + if (preferred_family == AF_UNSPEC) { + if (!(current_filter.dbs&~UNIX_DBM)) + preferred_family = AF_UNIX; + else if (!(current_filter.dbs&~PACKET_DBM)) + preferred_family = AF_PACKET; + else if (!(current_filter.dbs&~(1<<NETLINK_DB))) + preferred_family = AF_NETLINK; + } + + if (preferred_family != AF_UNSPEC) { + int mask2; + if (preferred_family == AF_INET || + preferred_family == AF_INET6) { + mask2= (1<<TCP_DB); + if (!do_default) + mask2 = (1<<UDP_DB)|(1<<RAW_DB); + } else if (preferred_family == AF_PACKET) { + mask2 = PACKET_DBM; + } else if (preferred_family == AF_UNIX) { + mask2 = UNIX_DBM; + } else if (preferred_family == AF_NETLINK) { + mask2 = (1<<NETLINK_DB); + } else { + mask2 = 0; + } + + if (do_default) + current_filter.dbs = mask2; + else + current_filter.dbs &= mask2; + current_filter.families = (1<<preferred_family); + } else { + if (!do_default) + current_filter.families = ~0; + else + current_filter.families = default_filter.families; + } + if (current_filter.dbs == 0) { + fprintf(stderr, "ss: no socket tables to show with such filter.\n"); + exit(0); + } + if (current_filter.families == 0) { + fprintf(stderr, "ss: no families to show with such filter.\n"); + exit(0); + } + + if (resolve_services && resolve_hosts && + (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)))) + init_service_resolver(); + + /* Now parse filter... */ + if (argc == 0 && filter_fp) { + if (ssfilter_parse(¤t_filter.f, 0, NULL, filter_fp)) + usage(); + } + + while (argc > 0) { + if (strcmp(*argv, "state") == 0) { + NEXT_ARG(); + if (!saw_states) + current_filter.states = 0; + current_filter.states |= scan_state(*argv); + saw_states = 1; + } else if (strcmp(*argv, "exclude") == 0 || + strcmp(*argv, "excl") == 0) { + NEXT_ARG(); + if (!saw_states) + current_filter.states = SS_ALL; + current_filter.states &= ~scan_state(*argv); + saw_states = 1; + } else { + if (ssfilter_parse(¤t_filter.f, argc, argv, filter_fp)) + usage(); + break; + } + argc--; argv++; + } + + if (current_filter.states == 0) { + fprintf(stderr, "ss: no socket states to show with such filter.\n"); + exit(0); + } + + if (dump_tcpdiag) { + FILE *dump_fp = stdout; + if (!(current_filter.dbs & (1<<TCP_DB))) { + fprintf(stderr, "ss: tcpdiag dump requested and no tcp in filter.\n"); + exit(0); + } + if (dump_tcpdiag[0] != '-') { + dump_fp = fopen(dump_tcpdiag, "w"); + if (!dump_tcpdiag) { + perror("fopen dump file"); + exit(-1); + } + } + tcp_show_netlink(¤t_filter, dump_fp); + fflush(dump_fp); + exit(0); + } + + netid_width = 0; + if (current_filter.dbs&(current_filter.dbs-1)) + netid_width = 5; + + state_width = 0; + if (current_filter.states&(current_filter.states-1)) + state_width = 10; + + screen_width = 80; + if (isatty(STDOUT_FILENO)) { + struct winsize w; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if (w.ws_col > 0) + screen_width = w.ws_col; + } + } + + addrp_width = screen_width; + addrp_width -= netid_width+1; + addrp_width -= state_width+1; + addrp_width -= 14; + + if (addrp_width&1) { + if (netid_width) + netid_width++; + else if (state_width) + state_width++; + } + + addrp_width /= 2; + addrp_width--; + + serv_width = resolve_services ? 7 : 5; + + if (addrp_width < 15+serv_width+1) + addrp_width = 15+serv_width+1; + + addr_width = addrp_width - serv_width - 1; + + if (netid_width) + printf("%-*s ", netid_width, "Netid"); + if (state_width) + printf("%-*s ", state_width, "State"); + printf("%-6s %-6s ", "Recv-Q", "Send-Q"); + + printf("%*s:%-*s %*s:%-*s\n", + addr_width, "Local Address", serv_width, "Port", + addr_width, "Peer Address", serv_width, "Port"); + +//printf("%08x %08x %08x\n", current_filter.dbs, current_filter.states, current_filter.families); + fflush(stdout); + + if (current_filter.dbs & (1<<NETLINK_DB)) + netlink_show(¤t_filter); + if (current_filter.dbs & PACKET_DBM) + packet_show(¤t_filter); + if (current_filter.dbs & UNIX_DBM) + unix_show(¤t_filter); + if (current_filter.dbs & (1<<RAW_DB)) + raw_show(¤t_filter); + if (current_filter.dbs & (1<<UDP_DB)) + udp_show(¤t_filter); + if (current_filter.dbs & (1<<TCP_DB)) + tcp_show(¤t_filter); + return 0; +} diff --git a/misc/ss.o b/misc/ss.o new file mode 100644 index 0000000000000000000000000000000000000000..18ee0d819dc02175e47567ac2c451013732facb2 GIT binary patch literal 68184 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 literal 0 HcmV?d00001 diff --git a/misc/ssfilter.c b/misc/ssfilter.c new file mode 100644 index 0000000..fa73a87 --- /dev/null +++ b/misc/ssfilter.c @@ -0,0 +1,1581 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + 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, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + HOSTCOND = 258, + DCOND = 259, + SCOND = 260, + DPORT = 261, + SPORT = 262, + LEQ = 263, + GEQ = 264, + NEQ = 265, + AUTOBOUND = 266 + }; +#endif +#define HOSTCOND 258 +#define DCOND 259 +#define SCOND 260 +#define DPORT 261 +#define SPORT 262 +#define LEQ 263 +#define GEQ 264 +#define NEQ 265 +#define AUTOBOUND 266 + + + + +/* Copy the first part of user declarations. */ +#line 1 "ssfilter.y" + + +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include "ssfilter.h" + +typedef struct ssfilter * ssfilter_t; + +#define YYSTYPE ssfilter_t + +static struct ssfilter * alloc_node(int type, void *pred) +{ + struct ssfilter *n = malloc(sizeof(*n)); + if (n == NULL) + abort(); + n->type = type; + n->pred = pred; + n->post = NULL; + return n; +} + +static char **yy_argv; +static int yy_argc; +static FILE *yy_fp; +static ssfilter_t *yy_ret; + +static int yylex(void); + +static void yyerror(char *s) +{ + fprintf(stderr, "ss: bison bellows (while parsing filter): \"%s!\"", s); +} + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +typedef int YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 146 "ssfilter.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# endif +# else +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 90 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 20 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 4 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 24 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 46 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 266 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 14, 2, 2, 2, 2, 13, 2, + 18, 19, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 16, 17, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 12, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 6, 8, 9, 12, 15, 19, 23, + 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, + 65, 69, 72, 76, 79 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 21, 0, -1, 22, 23, -1, 22, -1, -1, 4, + 3, -1, 5, 3, -1, 6, 9, 3, -1, 6, + 8, 3, -1, 6, 15, 3, -1, 6, 16, 3, + -1, 6, 17, 3, -1, 6, 10, 3, -1, 7, + 9, 3, -1, 7, 8, 3, -1, 7, 15, 3, + -1, 7, 16, 3, -1, 7, 17, 3, -1, 7, + 10, 3, -1, 11, -1, 23, 12, 23, -1, 23, + 23, -1, 23, 13, 23, -1, 14, 23, -1, 18, + 23, 19, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned char yyrline[] = +{ + 0, 44, 44, 49, 51, 53, 57, 61, 65, 69, + 73, 77, 81, 86, 90, 94, 98, 102, 106, 111, + 115, 120, 125, 131, 135 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "HOSTCOND", "DCOND", "SCOND", "DPORT", + "SPORT", "LEQ", "GEQ", "NEQ", "AUTOBOUND", "'|'", "'&'", "'!'", "'>'", + "'<'", "'='", "'('", "')'", "$accept", "applet", "null", "expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 124, 38, 33, 62, 60, 61, 40, 41 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 20, 21, 21, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 2, 1, 0, 2, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, + 3, 2, 3, 2, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 4, 0, 3, 1, 0, 0, 0, 0, 19, 0, + 0, 2, 5, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, + 21, 8, 7, 12, 9, 10, 11, 14, 13, 18, + 15, 16, 17, 24, 20, 22 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 1, 2, 30 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -3 +static const yysigned_char yypact[] = +{ + -3, 1, 47, -3, 6, 11, 69, 73, -3, 47, + 47, 17, -3, -3, 12, 13, 16, 22, 29, 30, + 31, 37, 38, 39, 41, 44, 62, -1, 47, 47, + 17, -3, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, -3, -3, 32, 47 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -3, -3, -3, -2 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yysigned_char yytable[] = +{ + 11, 3, 0, 4, 5, 6, 7, 26, 27, 12, + 8, 28, 29, 9, 13, 31, 32, 10, 43, 33, + 0, 4, 5, 6, 7, 34, 44, 45, 8, 28, + 29, 9, 35, 36, 37, 10, 4, 5, 6, 7, + 38, 39, 40, 8, 41, 29, 9, 42, 0, 0, + 10, 4, 5, 6, 7, 0, 0, 0, 8, 0, + 0, 9, 0, 0, 0, 10, 4, 5, 6, 7, + 0, 0, 0, 8, 0, 0, -1, 14, 15, 16, + 10, 20, 21, 22, 17, 18, 19, 0, 23, 24, + 25 +}; + +static const yysigned_char yycheck[] = +{ + 2, 0, -1, 4, 5, 6, 7, 9, 10, 3, + 11, 12, 13, 14, 3, 3, 3, 18, 19, 3, + -1, 4, 5, 6, 7, 3, 28, 29, 11, 12, + 13, 14, 3, 3, 3, 18, 4, 5, 6, 7, + 3, 3, 3, 11, 3, 13, 14, 3, -1, -1, + 18, 4, 5, 6, 7, -1, -1, -1, 11, -1, + -1, 14, -1, -1, -1, 18, 4, 5, 6, 7, + -1, -1, -1, 11, -1, -1, 14, 8, 9, 10, + 18, 8, 9, 10, 15, 16, 17, -1, 15, 16, + 17 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 21, 22, 0, 4, 5, 6, 7, 11, 14, + 18, 23, 3, 3, 8, 9, 10, 15, 16, 17, + 8, 9, 10, 15, 16, 17, 23, 23, 12, 13, + 23, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 19, 23, 23 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + ((Current).first_line = (Rhs)[1].first_line, \ + (Current).first_column = (Rhs)[1].first_column, \ + (Current).last_line = (Rhs)[N].last_line, \ + (Current).last_column = (Rhs)[N].last_column) +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 45 "ssfilter.y" + { + *yy_ret = yyvsp[0]; + yyval = yyvsp[0]; + ;} + break; + + case 4: +#line 51 "ssfilter.y" + { yyval = NULL; ;} + break; + + case 5: +#line 54 "ssfilter.y" + { + yyval = alloc_node(SSF_DCOND, yyvsp[0]); + ;} + break; + + case 6: +#line 58 "ssfilter.y" + { + yyval = alloc_node(SSF_SCOND, yyvsp[0]); + ;} + break; + + case 7: +#line 62 "ssfilter.y" + { + yyval = alloc_node(SSF_D_GE, yyvsp[0]); + ;} + break; + + case 8: +#line 66 "ssfilter.y" + { + yyval = alloc_node(SSF_D_LE, yyvsp[0]); + ;} + break; + + case 9: +#line 70 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_D_LE, yyvsp[0])); + ;} + break; + + case 10: +#line 74 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_D_GE, yyvsp[0])); + ;} + break; + + case 11: +#line 78 "ssfilter.y" + { + yyval = alloc_node(SSF_DCOND, yyvsp[0]); + ;} + break; + + case 12: +#line 82 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_DCOND, yyvsp[0])); + ;} + break; + + case 13: +#line 87 "ssfilter.y" + { + yyval = alloc_node(SSF_S_GE, yyvsp[0]); + ;} + break; + + case 14: +#line 91 "ssfilter.y" + { + yyval = alloc_node(SSF_S_LE, yyvsp[0]); + ;} + break; + + case 15: +#line 95 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_S_LE, yyvsp[0])); + ;} + break; + + case 16: +#line 99 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_S_GE, yyvsp[0])); + ;} + break; + + case 17: +#line 103 "ssfilter.y" + { + yyval = alloc_node(SSF_SCOND, yyvsp[0]); + ;} + break; + + case 18: +#line 107 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, yyvsp[0])); + ;} + break; + + case 19: +#line 112 "ssfilter.y" + { + yyval = alloc_node(SSF_S_AUTO, NULL); + ;} + break; + + case 20: +#line 116 "ssfilter.y" + { + yyval = alloc_node(SSF_OR, yyvsp[-2]); + yyval->post = yyvsp[0]; + ;} + break; + + case 21: +#line 121 "ssfilter.y" + { + yyval = alloc_node(SSF_AND, yyvsp[-1]); + yyval->post = yyvsp[0]; + ;} + break; + + case 22: +#line 127 "ssfilter.y" + { + yyval = alloc_node(SSF_AND, yyvsp[-2]); + yyval->post = yyvsp[0]; + ;} + break; + + case 23: +#line 132 "ssfilter.y" + { + yyval = alloc_node(SSF_NOT, yyvsp[0]); + ;} + break; + + case 24: +#line 136 "ssfilter.y" + { + yyval = yyvsp[-1]; + ;} + break; + + + } + +/* Line 1000 of yacc.c. */ +#line 1219 "ssfilter.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + } + } + else + { + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + + yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 140 "ssfilter.y" + + +static char *get_token_from_line(char **ptr) +{ + char *tok, *cp = *ptr; + + while (*cp == ' ' || *cp == '\t') cp++; + + if (*cp == 0) { + *ptr = cp; + return NULL; + } + + tok = cp; + + while (*cp != 0 && *cp != ' ' && *cp != '\t') { + /* Backslash escapes everything. */ + if (*cp == '\\') { + char *tp; + for (tp = cp; tp != tok; tp--) + *tp = *(tp-1); + cp++; + tok++; + if (*cp == 0) + break; + } + cp++; + } + if (*cp) + *cp++ = 0; + *ptr = cp; + return tok; +} + +int yylex(void) +{ + static char argbuf[1024]; + static char *tokptr = argbuf; + static int argc; + char *curtok; + + do { + while (*tokptr == 0) { + tokptr = NULL; + if (argc < yy_argc) { + tokptr = yy_argv[argc]; + argc++; + } else if (yy_fp) { + while (tokptr == NULL) { + if (fgets(argbuf, sizeof(argbuf)-1, yy_fp) == NULL) + return 0; + argbuf[sizeof(argbuf)-1] = 0; + if (strlen(argbuf) == sizeof(argbuf) - 1) { + fprintf(stderr, "Too long line in filter"); + exit(-1); + } + if (argbuf[strlen(argbuf)-1] == '\n') + argbuf[strlen(argbuf)-1] = 0; + if (argbuf[0] == '#' || argbuf[0] == '0') + continue; + tokptr = argbuf; + } + } else { + return 0; + } + } + } while ((curtok = get_token_from_line(&tokptr)) == NULL); + + if (strcmp(curtok, "!") == 0 || + strcmp(curtok, "not") == 0) + return '!'; + if (strcmp(curtok, "&") == 0 || + strcmp(curtok, "&&") == 0 || + strcmp(curtok, "and") == 0) + return '&'; + if (strcmp(curtok, "|") == 0 || + strcmp(curtok, "||") == 0 || + strcmp(curtok, "or") == 0) + return '|'; + if (strcmp(curtok, "(") == 0) + return '('; + if (strcmp(curtok, ")") == 0) + return ')'; + if (strcmp(curtok, "dst") == 0) + return DCOND; + if (strcmp(curtok, "src") == 0) + return SCOND; + if (strcmp(curtok, "dport") == 0) + return DPORT; + if (strcmp(curtok, "sport") == 0) + return SPORT; + if (strcmp(curtok, ">=") == 0 || + strcmp(curtok, "ge") == 0 || + strcmp(curtok, "geq") == 0) + return GEQ; + if (strcmp(curtok, "<=") == 0 || + strcmp(curtok, "le") == 0 || + strcmp(curtok, "leq") == 0) + return LEQ; + if (strcmp(curtok, "!=") == 0 || + strcmp(curtok, "ne") == 0 || + strcmp(curtok, "neq") == 0) + return NEQ; + if (strcmp(curtok, "=") == 0 || + strcmp(curtok, "==") == 0 || + strcmp(curtok, "eq") == 0) + return '='; + if (strcmp(curtok, ">") == 0 || + strcmp(curtok, "gt") == 0) + return '>'; + if (strcmp(curtok, "<") == 0 || + strcmp(curtok, "lt") == 0) + return '<'; + if (strcmp(curtok, "autobound") == 0) + return AUTOBOUND; + yylval = (void*)parse_hostcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse dst/src address.\n"); + exit(1); + } + return HOSTCOND; +} + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp) +{ + yy_argc = argc; + yy_argv = argv; + yy_fp = fp; + yy_ret = f; + + if (yyparse()) { + fprintf(stderr, " Sorry.\n"); + return -1; + } + return 0; +} + + diff --git a/misc/ssfilter.h b/misc/ssfilter.h new file mode 100644 index 0000000..00b92e3 --- /dev/null +++ b/misc/ssfilter.h @@ -0,0 +1,21 @@ +#define SSF_DCOND 0 +#define SSF_SCOND 1 +#define SSF_OR 2 +#define SSF_AND 3 +#define SSF_NOT 4 +#define SSF_D_GE 5 +#define SSF_D_LE 6 +#define SSF_S_GE 7 +#define SSF_S_LE 8 +#define SSF_S_AUTO 9 + +struct ssfilter +{ + int type; + struct ssfilter *post; + struct ssfilter *pred; +}; + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp); +void *parse_hostcond(char*); + diff --git a/misc/ssfilter.o b/misc/ssfilter.o new file mode 100644 index 0000000000000000000000000000000000000000..477cb5b7a8d4fc62a884a0ec2369f6dfa28db52b GIT binary patch literal 10032 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 literal 0 HcmV?d00001 diff --git a/misc/ssfilter.y b/misc/ssfilter.y new file mode 100644 index 0000000..8be5368 --- /dev/null +++ b/misc/ssfilter.y @@ -0,0 +1,275 @@ +%{ + +#include <stdio.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include "ssfilter.h" + +typedef struct ssfilter * ssfilter_t; + +#define YYSTYPE ssfilter_t + +static struct ssfilter * alloc_node(int type, void *pred) +{ + struct ssfilter *n = malloc(sizeof(*n)); + if (n == NULL) + abort(); + n->type = type; + n->pred = pred; + n->post = NULL; + return n; +} + +static char **yy_argv; +static int yy_argc; +static FILE *yy_fp; +static ssfilter_t *yy_ret; + +static int yylex(void); + +static void yyerror(char *s) +{ + fprintf(stderr, "ss: bison bellows (while parsing filter): \"%s!\"", s); +} + +%} + +%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND +%left '|' +%left '&' +%nonassoc '!' + +%% +applet: null expr + { + *yy_ret = $2; + $$ = $2; + } + | null + ; +null: /* NOTHING */ { $$ = NULL; } + ; +expr: DCOND HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $2); + } + | SCOND HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $2); + } + | DPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_D_GE, $3); + } + | DPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_D_LE, $3); + } + | DPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_LE, $3)); + } + | DPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_D_GE, $3)); + } + | DPORT '=' HOSTCOND + { + $$ = alloc_node(SSF_DCOND, $3); + } + | DPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_DCOND, $3)); + } + + | SPORT GEQ HOSTCOND + { + $$ = alloc_node(SSF_S_GE, $3); + } + | SPORT LEQ HOSTCOND + { + $$ = alloc_node(SSF_S_LE, $3); + } + | SPORT '>' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_LE, $3)); + } + | SPORT '<' HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_S_GE, $3)); + } + | SPORT '=' HOSTCOND + { + $$ = alloc_node(SSF_SCOND, $3); + } + | SPORT NEQ HOSTCOND + { + $$ = alloc_node(SSF_NOT, alloc_node(SSF_SCOND, $3)); + } + + | AUTOBOUND + { + $$ = alloc_node(SSF_S_AUTO, NULL); + } + | expr '|' expr + { + $$ = alloc_node(SSF_OR, $1); + $$->post = $3; + } + | expr expr + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $2; + } + | expr '&' expr + + { + $$ = alloc_node(SSF_AND, $1); + $$->post = $3; + } + | '!' expr + { + $$ = alloc_node(SSF_NOT, $2); + } + | '(' expr ')' + { + $$ = $2; + } +; +%% + +static char *get_token_from_line(char **ptr) +{ + char *tok, *cp = *ptr; + + while (*cp == ' ' || *cp == '\t') cp++; + + if (*cp == 0) { + *ptr = cp; + return NULL; + } + + tok = cp; + + while (*cp != 0 && *cp != ' ' && *cp != '\t') { + /* Backslash escapes everything. */ + if (*cp == '\\') { + char *tp; + for (tp = cp; tp != tok; tp--) + *tp = *(tp-1); + cp++; + tok++; + if (*cp == 0) + break; + } + cp++; + } + if (*cp) + *cp++ = 0; + *ptr = cp; + return tok; +} + +int yylex(void) +{ + static char argbuf[1024]; + static char *tokptr = argbuf; + static int argc; + char *curtok; + + do { + while (*tokptr == 0) { + tokptr = NULL; + if (argc < yy_argc) { + tokptr = yy_argv[argc]; + argc++; + } else if (yy_fp) { + while (tokptr == NULL) { + if (fgets(argbuf, sizeof(argbuf)-1, yy_fp) == NULL) + return 0; + argbuf[sizeof(argbuf)-1] = 0; + if (strlen(argbuf) == sizeof(argbuf) - 1) { + fprintf(stderr, "Too long line in filter"); + exit(-1); + } + if (argbuf[strlen(argbuf)-1] == '\n') + argbuf[strlen(argbuf)-1] = 0; + if (argbuf[0] == '#' || argbuf[0] == '0') + continue; + tokptr = argbuf; + } + } else { + return 0; + } + } + } while ((curtok = get_token_from_line(&tokptr)) == NULL); + + if (strcmp(curtok, "!") == 0 || + strcmp(curtok, "not") == 0) + return '!'; + if (strcmp(curtok, "&") == 0 || + strcmp(curtok, "&&") == 0 || + strcmp(curtok, "and") == 0) + return '&'; + if (strcmp(curtok, "|") == 0 || + strcmp(curtok, "||") == 0 || + strcmp(curtok, "or") == 0) + return '|'; + if (strcmp(curtok, "(") == 0) + return '('; + if (strcmp(curtok, ")") == 0) + return ')'; + if (strcmp(curtok, "dst") == 0) + return DCOND; + if (strcmp(curtok, "src") == 0) + return SCOND; + if (strcmp(curtok, "dport") == 0) + return DPORT; + if (strcmp(curtok, "sport") == 0) + return SPORT; + if (strcmp(curtok, ">=") == 0 || + strcmp(curtok, "ge") == 0 || + strcmp(curtok, "geq") == 0) + return GEQ; + if (strcmp(curtok, "<=") == 0 || + strcmp(curtok, "le") == 0 || + strcmp(curtok, "leq") == 0) + return LEQ; + if (strcmp(curtok, "!=") == 0 || + strcmp(curtok, "ne") == 0 || + strcmp(curtok, "neq") == 0) + return NEQ; + if (strcmp(curtok, "=") == 0 || + strcmp(curtok, "==") == 0 || + strcmp(curtok, "eq") == 0) + return '='; + if (strcmp(curtok, ">") == 0 || + strcmp(curtok, "gt") == 0) + return '>'; + if (strcmp(curtok, "<") == 0 || + strcmp(curtok, "lt") == 0) + return '<'; + if (strcmp(curtok, "autobound") == 0) + return AUTOBOUND; + yylval = (void*)parse_hostcond(curtok); + if (yylval == NULL) { + fprintf(stderr, "Cannot parse dst/src address.\n"); + exit(1); + } + return HOSTCOND; +} + +int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp) +{ + yy_argc = argc; + yy_argv = argv; + yy_fp = fp; + yy_ret = f; + + if (yyparse()) { + fprintf(stderr, " Sorry.\n"); + return -1; + } + return 0; +} diff --git a/netem/Makefile b/netem/Makefile new file mode 100644 index 0000000..881ed9f --- /dev/null +++ b/netem/Makefile @@ -0,0 +1,27 @@ +DISTGEN = maketable normal pareto paretonormal +DISTDATA = normal.dist pareto.dist paretonormal.dist experimental.dist + +LDLIBS += -lm + +%.dist: %.c + $(HOSTCC) $(CFLAGS) -o $* $< -lm + ./$* >$@ + +%.dist: %.dat + ./maketable $< >$@ + +all: $(DISTGEN) $(DISTDATA) + +install: all + mkdir -p $(DESTDIR)/usr/lib/tc + for i in $(DISTDATA); \ + do install -m 755 $$i $(DESTDIR)/usr/lib/tc; \ + done + +clean: + rm -f $(DISTDATA) $(DISTGEN) + +maketable: maketable.c + $(HOSTCC) $(CFLAGS) -o $@ $< -lm + + diff --git a/netem/README.distribution b/netem/README.distribution new file mode 100644 index 0000000..23f7ecb --- /dev/null +++ b/netem/README.distribution @@ -0,0 +1,97 @@ +Notes about distribution tables from Nistnet +------------------------------------------------------------------------------- +I. About the distribution tables + +The table used for "synthesizing" the distribution is essentially a scaled, +translated, inverse to the cumulative distribution function. + +Here's how to think about it: Let F() be the cumulative distribution +function for a probability distribution X. We'll assume we've scaled +things so that X has mean 0 and standard deviation 1, though that's not +so important here. Then: + + F(x) = P(X <= x) = \int_{-inf}^x f + +where f is the probability density function. + +F is monotonically increasing, so has an inverse function G, with range +0 to 1. Here, G(t) = the x such that P(X <= x) = t. (In general, G may +have singularities if X has point masses, i.e., points x such that +P(X = x) > 0.) + +Now we create a tabular representation of G as follows: Choose some table +size N, and for the ith entry, put in G(i/N). Let's call this table T. + +The claim now is, I can create a (discrete) random variable Y whose +distribution has the same approximate "shape" as X, simply by letting +Y = T(U), where U is a discrete uniform random variable with range 1 to N. +To see this, it's enough to show that Y's cumulative distribution function, +(let's call it H), is a discrete approximation to F. But + + H(x) = P(Y <= x) + = (# of entries in T <= x) / N -- as Y chosen uniformly from T + = i/N, where i is the largest integer such that G(i/N) <= x + = i/N, where i is the largest integer such that i/N <= F(x) + -- since G and F are inverse functions (and F is + increasing) + = floor(N*F(x))/N + +as desired. + +II. How to create distribution tables (in theory) + +How can we create this table in practice? In some cases, F may have a +simple expression which allows evaluating its inverse directly. The +pareto distribution is one example of this. In other cases, and +especially for matching an experimentally observed distribution, it's +easiest simply to create a table for F and "invert" it. Here, we give +a concrete example, namely how the new "experimental" distribution was +created. + +1. Collect enough data points to characterize the distribution. Here, I +collected 25,000 "ping" roundtrip times to a "distant" point (time.nist.gov). +That's far more data than is really necessary, but it was fairly painless to +collect it, so... + +2. Normalize the data so that it has mean 0 and standard deviation 1. + +3. Determine the cumulative distribution. The code I wrote creates a table +covering the range -10 to +10, with granularity .00005. Obviously, this +is absurdly over-precise, but since it's a one-time only computation, I +figured it hardly mattered. + +4. Invert the table: for each table entry F(x) = y, make the y*TABLESIZE +(here, 4096) entry be x*TABLEFACTOR (here, 8192). This creates a table +for the ("normalized") inverse of size TABLESIZE, covering its domain 0 +to 1 with granularity 1/TABLESIZE. Note that even with the granularity +used in creating the table for F, it's possible not all the entries in +the table for G will be filled in. So, make a pass through the +inverse's table, filling in any missing entries by linear interpolation. + +III. How to create distribution tables (in practice) + +If you want to do all this yourself, I've provided several tools to help: + +1. maketable does the steps 2-4 above, and then generates the appropriate +header file. So if you have your own time distribution, you can generate +the header simply by: + + maketable < time.values > header.h + +2. As explained in the other README file, the somewhat sleazy way I have +of generating correlated values needs correction. You can generate your +own correction tables by compiling makesigtable and makemutable with +your header file. Check the Makefile to see how this is done. + +3. Warning: maketable, makesigtable and especially makemutable do +enormous amounts of floating point arithmetic. Don't try running +these on an old 486. (NIST Net itself will run fine on such a +system, since in operation, it just needs to do a few simple integral +calculations. But getting there takes some work.) + +4. The tables produced are all normalized for mean 0 and standard +deviation 1. How do you know what values to use for real? Here, I've +provided a simple "stats" utility. Give it a series of floating point +values, and it will return their mean (mu), standard deviation (sigma), +and correlation coefficient (rho). You can then plug these values +directly into NIST Net. diff --git a/netem/experimental.dat b/netem/experimental.dat new file mode 100644 index 0000000..3663a3e --- /dev/null +++ b/netem/experimental.dat @@ -0,0 +1,13448 @@ +211.6 +205.6 +203.0 +218.6 +213.9 +199.1 +208.7 +207.7 +203.4 +201.7 +200.3 +213.8 +213.4 +209.8 +204.3 +201.8 +196.3 +216.2 +208.9 +202.4 +205.2 +211.1 +210.9 +208.5 +199.9 +211.6 +211.9 +204.6 +215.4 +202.5 +206.5 +201.1 +198.4 +220.2 +203.7 +219.5 +199.1 +207.6 +205.3 +202.3 +219.7 +230.0 +211.0 +202.7 +209.9 +215.4 +202.9 +209.6 +200.5 +197.3 +212.3 +207.6 +210.5 +202.7 +205.7 +211.2 +208.0 +211.0 +209.4 +204.8 +204.8 +208.7 +210.1 +205.3 +202.5 +210.4 +209.4 +204.5 +204.7 +215.0 +202.6 +209.9 +220.2 +203.8 +206.3 +199.4 +221.8 +200.0 +199.6 +209.3 +206.2 +215.8 +196.9 +211.6 +198.4 +201.2 +209.4 +204.3 +219.0 +212.7 +214.6 +196.3 +202.0 +201.9 +197.5 +229.5 +207.5 +213.8 +209.2 +212.9 +193.9 +200.8 +208.6 +196.8 +201.3 +204.9 +204.7 +209.5 +211.3 +215.3 +203.7 +190.1 +235.6 +203.8 +210.0 +209.7 +214.3 +213.0 +206.3 +197.7 +208.2 +226.3 +216.5 +198.0 +201.3 +211.3 +195.8 +210.9 +208.1 +201.2 +201.7 +213.1 +207.9 +206.6 +207.1 +202.2 +199.6 +205.5 +207.3 +219.7 +204.1 +204.4 +209.0 +212.7 +196.4 +214.0 +208.8 +209.7 +217.2 +196.2 +195.0 +227.7 +207.2 +233.3 +207.9 +204.0 +194.4 +219.2 +208.7 +198.6 +205.0 +204.0 +223.7 +207.4 +209.2 +208.7 +205.4 +212.8 +207.8 +203.0 +204.1 +221.0 +198.4 +217.7 +218.4 +374.2 +220.1 +210.8 +212.1 +214.3 +213.3 +210.3 +202.4 +209.7 +218.1 +205.0 +204.5 +220.3 +209.8 +218.3 +216.6 +206.0 +208.9 +221.0 +213.0 +202.1 +204.2 +220.6 +212.4 +226.1 +208.8 +206.1 +220.7 +219.3 +210.9 +211.2 +213.0 +201.4 +210.5 +206.2 +201.9 +224.5 +219.3 +201.1 +195.6 +223.6 +196.7 +213.7 +202.3 +215.6 +211.4 +209.6 +207.6 +212.4 +203.4 +205.4 +216.1 +216.7 +205.3 +213.9 +208.9 +208.4 +205.1 +199.3 +200.6 +199.1 +203.2 +207.6 +203.8 +201.9 +208.5 +196.4 +213.6 +217.6 +201.5 +210.1 +213.5 +203.8 +214.1 +211.9 +201.5 +186.9 +199.7 +209.1 +200.2 +205.8 +206.7 +200.0 +198.1 +209.3 +207.8 +208.7 +208.0 +208.6 +231.3 +214.5 +210.1 +200.8 +208.9 +216.9 +205.7 +214.9 +236.8 +200.9 +219.1 +204.6 +210.0 +214.0 +222.6 +209.6 +207.0 +196.3 +207.7 +207.9 +208.0 +220.2 +198.2 +204.9 +204.1 +201.0 +204.8 +213.3 +203.9 +222.5 +205.2 +203.5 +209.7 +212.1 +210.1 +221.1 +210.2 +208.0 +201.4 +209.0 +211.9 +201.6 +214.4 +199.6 +198.8 +210.2 +207.3 +206.5 +204.8 +196.3 +199.8 +206.4 +195.3 +202.8 +202.7 +203.8 +211.2 +208.4 +198.6 +202.0 +214.9 +204.2 +201.1 +195.9 +196.1 +211.2 +197.0 +207.7 +196.6 +205.7 +211.4 +201.4 +205.0 +195.5 +198.9 +214.4 +207.3 +204.2 +207.2 +198.5 +220.7 +214.1 +213.2 +207.7 +203.6 +265.8 +221.0 +213.1 +195.4 +197.3 +213.0 +207.7 +206.0 +198.4 +202.3 +213.9 +218.6 +207.6 +206.1 +212.8 +216.8 +213.7 +209.8 +198.1 +202.4 +205.3 +207.0 +209.2 +209.9 +204.4 +199.6 +205.5 +203.9 +216.0 +213.1 +202.4 +199.0 +219.5 +193.9 +197.3 +212.2 +216.7 +217.5 +201.0 +206.2 +202.9 +211.3 +203.1 +218.0 +208.6 +217.8 +209.0 +211.8 +220.1 +212.7 +207.2 +221.2 +215.2 +196.9 +216.6 +203.1 +207.1 +216.7 +206.7 +215.0 +219.3 +204.3 +219.6 +207.1 +211.8 +210.2 +217.2 +207.9 +219.9 +205.4 +201.1 +214.1 +205.8 +212.5 +222.8 +211.9 +217.4 +203.8 +222.9 +206.6 +207.6 +197.5 +206.2 +218.5 +220.3 +207.7 +203.5 +226.4 +216.8 +206.0 +193.2 +198.2 +201.3 +202.4 +208.5 +212.6 +205.0 +202.2 +210.0 +202.4 +203.9 +193.3 +212.4 +203.4 +212.1 +206.1 +206.9 +207.0 +216.1 +201.1 +204.7 +202.4 +207.5 +203.9 +200.9 +210.0 +207.1 +217.2 +197.4 +199.2 +210.8 +209.2 +218.4 +200.2 +211.7 +213.6 +203.3 +197.9 +203.0 +204.2 +207.9 +209.4 +225.4 +237.3 +209.5 +208.2 +207.5 +207.0 +203.0 +219.3 +228.3 +213.5 +205.1 +198.9 +212.7 +201.5 +210.0 +206.5 +203.3 +206.1 +210.1 +219.7 +206.8 +215.4 +220.4 +217.3 +211.4 +206.0 +208.3 +207.3 +205.5 +210.8 +209.3 +197.2 +207.2 +191.7 +204.2 +207.2 +216.1 +209.1 +203.8 +201.8 +208.7 +212.4 +214.5 +213.8 +201.3 +219.7 +214.8 +211.9 +223.8 +208.6 +203.5 +207.4 +207.0 +198.0 +208.2 +218.6 +205.1 +214.6 +215.2 +215.3 +204.3 +210.1 +221.9 +210.7 +198.2 +205.2 +201.1 +219.0 +207.2 +205.9 +203.8 +200.5 +217.5 +208.7 +208.4 +192.6 +211.0 +209.1 +206.5 +197.4 +202.1 +210.0 +198.3 +222.2 +211.9 +212.3 +222.2 +195.1 +200.7 +212.1 +208.3 +211.8 +211.7 +206.5 +211.8 +207.6 +214.2 +207.7 +204.7 +208.2 +208.4 +207.9 +212.1 +223.2 +206.3 +205.6 +201.8 +211.9 +207.6 +203.0 +221.2 +206.3 +222.4 +253.5 +204.4 +218.9 +211.9 +210.9 +214.0 +226.7 +214.4 +199.7 +213.8 +207.0 +201.8 +206.6 +203.1 +202.1 +203.6 +213.9 +196.9 +200.4 +204.6 +333.4 +204.5 +220.9 +207.3 +212.1 +203.7 +200.9 +198.2 +204.0 +201.4 +198.2 +209.6 +211.5 +201.2 +200.4 +207.4 +200.7 +213.8 +207.7 +188.0 +210.0 +210.5 +207.3 +198.6 +206.1 +186.9 +201.4 +204.0 +200.8 +207.8 +211.7 +198.7 +206.1 +213.0 +214.8 +212.8 +208.8 +210.4 +206.5 +210.1 +201.7 +202.7 +201.3 +194.1 +200.8 +196.8 +204.2 +217.5 +209.0 +198.7 +203.2 +213.8 +198.0 +207.1 +204.0 +215.3 +199.5 +214.1 +200.1 +206.9 +219.9 +204.8 +208.6 +207.8 +207.5 +203.8 +210.9 +210.6 +205.3 +202.1 +212.9 +214.8 +210.9 +217.2 +218.3 +221.5 +201.8 +212.7 +215.0 +206.7 +222.8 +210.9 +211.5 +202.0 +208.1 +268.9 +205.8 +204.0 +198.4 +206.3 +209.3 +206.4 +207.4 +226.9 +209.9 +199.6 +206.5 +210.9 +224.1 +211.9 +214.4 +212.2 +211.5 +209.4 +205.3 +204.8 +207.7 +208.9 +213.7 +201.0 +217.4 +198.1 +219.0 +206.5 +229.1 +220.1 +196.8 +203.1 +208.8 +201.7 +195.7 +207.0 +202.4 +206.6 +204.9 +196.6 +204.3 +198.6 +203.9 +215.8 +194.9 +202.7 +225.5 +205.9 +201.4 +213.1 +214.2 +218.8 +209.4 +204.4 +206.7 +209.8 +198.4 +211.8 +212.1 +209.1 +202.3 +213.7 +215.5 +218.3 +209.1 +216.6 +214.8 +206.4 +205.6 +214.4 +209.2 +211.7 +211.3 +211.0 +205.6 +204.2 +191.7 +213.8 +204.9 +205.3 +212.0 +199.9 +198.3 +211.8 +203.0 +212.2 +203.0 +201.8 +214.4 +214.1 +199.6 +205.3 +208.2 +196.7 +196.7 +209.1 +205.1 +212.5 +213.1 +197.3 +208.8 +218.0 +220.0 +198.4 +206.3 +206.9 +253.2 +194.3 +202.6 +210.6 +219.1 +197.8 +197.1 +194.0 +211.6 +209.6 +198.3 +213.0 +207.7 +207.0 +213.3 +206.9 +197.6 +204.8 +202.0 +200.0 +215.2 +204.5 +206.3 +206.7 +203.2 +194.9 +206.3 +209.9 +210.6 +214.2 +208.6 +207.4 +213.9 +210.4 +210.0 +200.6 +203.8 +202.7 +204.2 +202.7 +210.2 +192.5 +215.4 +211.7 +208.3 +204.8 +203.3 +197.7 +216.7 +200.9 +203.6 +208.6 +206.5 +209.9 +200.1 +198.4 +203.3 +210.4 +211.6 +202.0 +203.1 +204.0 +204.0 +215.0 +211.4 +202.0 +197.2 +197.6 +209.9 +205.4 +213.1 +199.1 +212.4 +216.1 +218.3 +214.6 +224.1 +206.9 +199.4 +213.4 +261.2 +199.4 +208.8 +209.9 +205.7 +203.1 +203.2 +204.6 +201.6 +210.6 +213.2 +214.8 +203.8 +204.9 +220.7 +201.5 +212.5 +216.8 +209.7 +203.1 +213.3 +204.7 +218.2 +215.5 +215.6 +211.6 +214.2 +205.1 +216.6 +216.3 +203.5 +200.8 +213.7 +221.9 +215.0 +210.2 +217.1 +214.7 +208.8 +217.4 +231.1 +213.7 +215.0 +213.5 +216.7 +207.7 +201.0 +210.4 +210.9 +206.7 +203.7 +199.2 +209.3 +206.3 +202.4 +210.1 +212.3 +202.2 +207.2 +197.8 +205.9 +202.0 +214.2 +203.5 +204.4 +200.0 +204.0 +193.8 +192.3 +229.0 +204.5 +194.8 +213.6 +215.9 +214.8 +221.6 +208.5 +201.5 +204.4 +206.4 +194.5 +199.4 +201.5 +209.7 +212.5 +202.1 +208.2 +205.4 +204.5 +199.4 +194.5 +199.6 +201.5 +206.2 +219.9 +198.5 +216.2 +195.7 +205.0 +208.0 +204.9 +195.9 +207.4 +216.9 +195.9 +204.4 +208.3 +206.1 +188.5 +202.3 +201.7 +200.5 +206.2 +191.5 +218.6 +206.5 +208.9 +209.9 +201.5 +212.7 +203.2 +209.7 +212.1 +208.4 +207.2 +206.5 +204.5 +222.7 +207.6 +207.4 +210.3 +212.2 +219.1 +215.2 +211.1 +205.9 +205.5 +205.9 +203.1 +205.4 +184.5 +205.0 +194.8 +213.5 +209.8 +195.4 +202.9 +205.3 +196.3 +202.0 +198.2 +201.5 +195.3 +230.9 +207.8 +212.6 +202.7 +204.8 +205.0 +202.8 +206.2 +200.2 +202.7 +203.5 +205.5 +196.9 +209.4 +212.1 +200.8 +205.0 +208.0 +207.1 +198.0 +204.8 +205.8 +200.9 +202.1 +202.4 +206.9 +209.1 +199.7 +197.1 +206.9 +200.2 +193.7 +195.0 +250.8 +207.5 +204.5 +208.8 +209.8 +194.8 +200.2 +205.1 +197.3 +208.3 +200.4 +204.7 +211.1 +203.4 +218.2 +194.6 +201.5 +202.2 +202.9 +198.8 +218.2 +201.7 +189.8 +210.1 +208.0 +204.3 +205.8 +204.2 +207.8 +200.2 +197.9 +198.9 +208.1 +202.4 +196.2 +195.5 +204.6 +211.0 +205.0 +193.6 +197.2 +198.6 +193.8 +198.9 +232.4 +201.8 +212.2 +208.6 +204.5 +199.3 +211.2 +203.1 +209.7 +214.3 +203.9 +200.3 +203.3 +206.1 +206.9 +209.1 +209.1 +199.3 +199.4 +198.8 +198.9 +199.9 +193.7 +204.6 +203.4 +199.7 +212.6 +200.7 +208.1 +198.8 +200.5 +209.2 +208.4 +205.7 +197.1 +202.6 +199.5 +208.4 +200.1 +204.9 +202.9 +201.5 +207.6 +200.6 +204.2 +210.0 +207.1 +205.1 +198.5 +204.9 +196.5 +208.0 +202.4 +202.7 +196.2 +206.9 +201.5 +203.3 +198.7 +211.9 +208.4 +206.7 +209.4 +204.0 +202.3 +205.0 +205.3 +206.0 +213.1 +205.7 +199.3 +206.2 +204.6 +209.3 +205.7 +202.7 +213.3 +202.3 +197.8 +196.5 +193.4 +211.6 +209.9 +195.5 +196.2 +210.2 +207.1 +207.0 +221.8 +217.2 +215.4 +207.0 +200.1 +207.5 +206.0 +200.7 +190.9 +209.8 +213.5 +206.3 +196.0 +213.1 +202.7 +211.6 +196.5 +209.9 +212.3 +199.9 +206.8 +225.1 +203.9 +204.3 +197.7 +203.5 +203.2 +193.5 +200.9 +201.4 +189.1 +203.9 +194.5 +205.4 +204.8 +204.9 +201.3 +208.4 +196.9 +206.8 +207.7 +201.6 +210.3 +211.6 +209.8 +200.2 +205.2 +197.6 +195.9 +212.8 +206.4 +201.0 +208.2 +207.5 +202.5 +193.3 +206.5 +221.2 +198.8 +216.6 +217.0 +209.1 +206.6 +197.7 +211.0 +199.9 +198.0 +210.4 +200.5 +211.7 +219.6 +206.8 +207.2 +210.6 +205.4 +203.8 +207.4 +206.2 +205.1 +208.7 +196.3 +204.7 +210.8 +214.4 +196.3 +206.5 +210.8 +193.2 +203.3 +203.9 +207.7 +194.9 +203.7 +195.5 +218.7 +201.1 +199.5 +207.6 +209.3 +207.5 +205.7 +203.9 +205.4 +201.3 +205.8 +205.4 +208.8 +214.3 +203.4 +207.5 +188.9 +205.5 +200.7 +212.5 +197.9 +219.0 +213.6 +197.3 +202.7 +216.3 +205.0 +210.2 +203.2 +203.9 +206.8 +213.6 +200.1 +204.4 +211.4 +213.4 +200.2 +208.4 +209.1 +198.8 +207.4 +195.0 +205.6 +200.5 +204.3 +201.9 +206.4 +199.0 +196.1 +207.6 +195.4 +197.2 +200.7 +190.8 +211.9 +191.5 +201.4 +193.5 +205.1 +206.8 +199.5 +207.4 +209.8 +199.1 +194.6 +201.6 +211.6 +206.8 +203.9 +196.8 +206.3 +210.1 +200.6 +227.4 +201.9 +210.8 +205.8 +217.2 +205.8 +196.1 +200.7 +213.8 +205.4 +211.6 +212.3 +213.6 +201.7 +199.9 +203.2 +212.6 +211.0 +208.1 +198.1 +201.7 +211.6 +207.4 +212.4 +207.3 +214.9 +214.5 +214.5 +202.7 +200.1 +206.4 +213.4 +189.7 +203.4 +202.2 +198.2 +206.5 +213.7 +207.6 +202.8 +209.2 +205.5 +196.4 +207.6 +207.4 +207.3 +188.8 +215.6 +195.4 +207.7 +208.2 +200.9 +208.4 +203.3 +210.8 +199.6 +208.3 +206.7 +201.6 +202.9 +197.5 +206.4 +209.0 +208.4 +211.6 +204.4 +210.0 +190.9 +199.3 +207.6 +202.5 +197.0 +200.8 +203.1 +204.0 +199.0 +208.0 +204.6 +196.6 +200.8 +205.2 +198.8 +203.0 +208.3 +200.1 +205.5 +203.7 +202.2 +203.8 +211.5 +201.8 +213.2 +207.4 +207.8 +202.2 +208.2 +204.2 +200.4 +186.1 +188.5 +220.4 +212.8 +193.3 +196.9 +203.0 +207.3 +202.4 +201.7 +204.8 +192.2 +218.7 +226.3 +209.5 +201.4 +207.3 +202.6 +210.7 +208.4 +208.4 +207.4 +210.4 +191.2 +203.6 +197.1 +207.5 +197.8 +206.2 +214.5 +208.2 +207.3 +204.7 +199.6 +206.3 +189.0 +214.4 +209.4 +208.1 +199.9 +190.5 +223.0 +198.8 +201.1 +192.4 +204.0 +209.0 +206.7 +204.3 +198.7 +210.9 +212.0 +204.8 +204.2 +199.5 +203.5 +203.0 +190.6 +207.9 +207.9 +193.2 +210.9 +200.1 +207.6 +193.6 +204.9 +197.7 +200.9 +213.0 +215.0 +204.4 +196.6 +209.6 +209.9 +199.8 +198.8 +202.1 +203.4 +205.4 +204.4 +196.2 +190.7 +210.9 +197.7 +194.7 +204.0 +201.5 +195.3 +209.0 +203.6 +196.1 +205.2 +206.7 +206.6 +191.4 +193.4 +206.6 +205.9 +207.9 +201.7 +213.3 +199.4 +202.8 +196.1 +208.3 +206.4 +205.2 +191.9 +207.3 +191.5 +210.8 +200.9 +210.4 +208.3 +211.0 +202.7 +198.8 +196.8 +202.7 +196.9 +214.6 +210.2 +226.1 +220.8 +213.5 +194.9 +210.4 +203.7 +203.7 +180.8 +213.7 +208.0 +209.8 +209.7 +213.8 +185.5 +208.5 +203.5 +212.8 +193.1 +199.2 +211.0 +217.4 +211.0 +202.7 +205.0 +208.6 +197.5 +197.1 +201.0 +195.9 +208.4 +205.7 +205.8 +194.0 +204.4 +194.5 +194.3 +200.1 +209.5 +218.0 +202.8 +197.5 +206.7 +199.8 +205.2 +201.4 +205.2 +186.0 +208.4 +218.4 +206.7 +201.9 +209.7 +208.0 +203.9 +193.1 +202.0 +198.0 +199.5 +211.0 +191.8 +198.7 +197.3 +195.6 +202.9 +203.4 +206.1 +205.6 +207.5 +220.8 +204.7 +207.7 +252.5 +203.9 +203.2 +201.3 +200.1 +201.1 +196.8 +197.6 +206.4 +209.6 +197.9 +199.4 +212.6 +205.4 +200.9 +197.5 +202.2 +199.5 +206.7 +215.1 +216.4 +221.9 +199.2 +246.4 +196.0 +205.1 +205.4 +207.8 +192.6 +204.6 +209.2 +213.4 +198.9 +205.3 +205.8 +201.1 +195.2 +199.4 +200.8 +210.5 +202.3 +217.9 +208.4 +220.8 +218.4 +195.7 +199.4 +198.8 +192.0 +210.9 +218.5 +194.5 +203.6 +195.0 +208.8 +197.4 +204.1 +200.7 +201.0 +206.6 +202.2 +208.7 +213.1 +198.3 +212.2 +201.9 +206.3 +203.4 +198.0 +198.0 +205.3 +199.6 +196.6 +202.8 +201.7 +208.7 +195.6 +199.4 +205.4 +205.2 +202.2 +193.3 +191.9 +195.1 +201.1 +210.5 +208.7 +196.9 +193.4 +200.8 +199.6 +204.1 +200.4 +197.6 +204.1 +206.9 +205.2 +206.9 +194.7 +200.4 +198.8 +201.7 +201.8 +207.0 +193.2 +199.9 +201.3 +192.5 +197.9 +206.9 +190.0 +203.8 +208.8 +200.9 +203.3 +194.5 +192.6 +204.9 +205.5 +196.6 +194.8 +197.9 +198.1 +211.2 +198.8 +202.2 +205.9 +199.5 +204.7 +201.6 +201.2 +203.4 +204.2 +190.7 +206.7 +205.4 +208.4 +203.1 +204.2 +198.4 +194.3 +191.6 +198.9 +203.5 +198.7 +192.2 +198.4 +194.5 +181.1 +200.9 +200.0 +209.2 +210.4 +200.0 +201.1 +193.9 +207.0 +193.4 +202.6 +192.8 +196.0 +203.8 +184.2 +179.3 +202.3 +191.4 +199.7 +195.4 +189.9 +197.0 +187.5 +192.1 +198.3 +202.2 +205.0 +212.3 +198.0 +205.5 +210.1 +197.6 +198.7 +206.6 +203.4 +194.3 +181.2 +199.0 +202.4 +189.1 +181.6 +200.4 +188.1 +180.1 +203.1 +201.1 +195.5 +201.6 +201.3 +197.6 +196.0 +205.5 +184.9 +186.5 +190.8 +188.6 +207.2 +199.5 +198.6 +199.8 +212.2 +208.1 +196.9 +199.6 +205.3 +196.9 +188.9 +205.4 +212.5 +197.5 +201.8 +188.8 +187.1 +199.9 +195.4 +188.7 +198.7 +185.0 +191.6 +193.3 +191.8 +209.4 +197.4 +195.2 +189.4 +189.7 +199.9 +199.3 +188.7 +188.3 +190.9 +181.6 +209.8 +194.6 +198.2 +199.9 +198.1 +186.8 +195.3 +190.9 +198.8 +189.3 +207.5 +179.2 +188.8 +185.6 +206.2 +184.8 +190.7 +203.5 +199.2 +202.0 +197.6 +197.2 +196.4 +210.4 +200.1 +194.8 +186.7 +198.2 +197.8 +186.5 +200.2 +192.7 +192.7 +190.4 +220.9 +207.5 +188.6 +198.5 +203.0 +202.2 +189.6 +177.3 +194.8 +195.2 +243.9 +196.5 +180.6 +214.6 +196.4 +220.6 +194.7 +200.5 +193.7 +199.7 +203.0 +201.4 +187.7 +199.8 +191.8 +203.9 +203.8 +191.3 +206.6 +201.7 +202.1 +202.6 +200.0 +203.6 +195.9 +204.8 +212.8 +199.2 +203.3 +206.6 +192.2 +205.0 +198.9 +205.3 +195.0 +198.1 +190.4 +203.7 +188.2 +204.2 +211.1 +192.5 +194.5 +198.3 +205.7 +198.5 +210.2 +206.8 +195.4 +200.8 +202.7 +220.0 +204.1 +209.5 +200.2 +187.1 +205.4 +202.6 +203.3 +214.1 +193.8 +207.4 +208.2 +204.9 +215.9 +202.6 +198.0 +193.8 +198.2 +206.2 +203.9 +190.6 +210.8 +195.6 +207.6 +206.6 +195.4 +189.9 +199.7 +203.1 +207.1 +192.2 +197.3 +197.6 +193.3 +207.9 +201.3 +206.8 +201.9 +195.7 +204.1 +201.1 +192.5 +206.7 +213.1 +195.2 +205.1 +196.2 +203.5 +195.5 +200.3 +194.7 +194.5 +200.6 +211.2 +202.1 +194.6 +199.9 +212.6 +206.8 +196.2 +205.8 +202.8 +201.6 +205.2 +205.8 +193.1 +202.0 +196.2 +208.1 +209.5 +199.8 +208.8 +192.3 +207.9 +201.3 +205.7 +205.9 +208.6 +210.3 +202.2 +212.1 +210.3 +199.6 +200.8 +209.1 +202.5 +215.0 +201.5 +209.2 +207.0 +215.3 +205.6 +213.7 +203.7 +199.8 +201.4 +194.7 +194.3 +188.7 +200.9 +203.8 +203.2 +212.5 +207.0 +211.3 +204.3 +204.5 +194.0 +210.7 +207.1 +207.5 +200.7 +200.8 +200.7 +200.1 +203.7 +191.1 +201.8 +194.8 +195.2 +197.4 +190.5 +192.7 +206.6 +200.8 +204.3 +206.5 +209.8 +202.5 +207.6 +198.4 +203.3 +202.1 +200.6 +198.4 +191.0 +203.5 +198.6 +184.3 +183.7 +189.1 +205.4 +187.8 +194.5 +199.2 +196.4 +210.9 +176.8 +191.6 +182.7 +181.3 +205.7 +203.2 +186.3 +187.6 +189.1 +180.8 +180.2 +187.6 +194.9 +192.8 +185.2 +198.3 +209.3 +177.5 +193.9 +193.1 +203.4 +192.1 +200.9 +182.6 +204.9 +197.6 +212.8 +206.9 +193.3 +201.0 +195.3 +197.1 +189.6 +198.5 +190.4 +188.8 +197.7 +189.9 +200.7 +196.8 +186.3 +181.5 +184.9 +200.2 +198.7 +205.8 +200.2 +198.3 +207.9 +206.1 +201.5 +197.8 +199.5 +198.1 +211.3 +201.6 +202.4 +196.0 +197.7 +209.2 +199.3 +205.5 +191.6 +206.4 +196.5 +209.5 +203.4 +201.4 +200.1 +205.2 +190.9 +205.1 +197.5 +196.1 +194.4 +194.7 +188.9 +180.8 +206.5 +199.8 +193.1 +195.2 +192.7 +199.2 +199.5 +188.1 +180.2 +191.0 +206.9 +208.2 +202.5 +200.0 +207.0 +201.6 +195.6 +195.6 +195.0 +196.3 +190.2 +194.0 +182.9 +192.1 +206.5 +181.4 +192.3 +199.6 +201.6 +192.4 +200.5 +207.1 +198.5 +198.8 +190.8 +200.3 +199.3 +200.5 +187.7 +208.3 +205.6 +189.3 +198.4 +204.9 +197.4 +198.7 +190.6 +214.5 +212.5 +207.6 +196.9 +183.4 +185.1 +205.8 +226.5 +202.5 +201.8 +202.9 +210.4 +189.7 +195.6 +198.7 +193.0 +198.8 +193.1 +202.5 +195.2 +195.0 +198.3 +203.6 +208.3 +195.9 +200.8 +189.4 +207.9 +182.8 +194.9 +199.7 +180.7 +187.2 +189.5 +196.1 +190.1 +192.5 +185.7 +212.2 +204.2 +191.9 +184.5 +182.5 +198.5 +191.0 +192.0 +195.6 +201.1 +193.7 +203.8 +200.5 +199.1 +190.3 +209.5 +195.0 +184.0 +193.6 +203.3 +191.4 +194.9 +195.5 +193.5 +182.7 +189.7 +196.1 +178.9 +199.5 +195.3 +185.9 +199.1 +210.0 +195.7 +193.8 +196.4 +195.3 +201.4 +209.5 +205.6 +197.5 +188.9 +193.8 +185.3 +193.3 +198.1 +201.4 +184.7 +182.5 +183.7 +185.5 +199.8 +200.3 +194.1 +176.9 +192.2 +200.0 +186.4 +191.6 +200.1 +202.3 +205.1 +186.4 +182.3 +194.7 +177.5 +201.4 +189.6 +195.5 +185.4 +194.8 +204.1 +188.0 +182.1 +181.7 +184.5 +234.2 +209.4 +193.3 +204.0 +184.7 +194.3 +193.4 +191.1 +188.3 +193.9 +198.2 +202.8 +198.1 +191.2 +200.9 +205.4 +203.6 +193.8 +215.8 +185.8 +195.4 +204.7 +190.3 +190.7 +177.7 +182.1 +193.2 +178.3 +199.3 +203.5 +187.3 +198.8 +187.8 +187.7 +186.7 +200.0 +190.0 +203.1 +181.7 +207.2 +183.8 +180.3 +193.5 +190.2 +193.7 +198.6 +195.6 +192.1 +200.5 +188.6 +190.9 +188.0 +192.8 +191.4 +179.9 +197.6 +200.6 +206.1 +201.3 +199.6 +198.8 +201.0 +180.2 +202.9 +197.3 +186.1 +200.5 +182.4 +192.7 +194.5 +182.8 +193.9 +195.1 +187.7 +201.0 +196.1 +194.0 +198.8 +192.8 +186.1 +200.6 +186.3 +187.6 +178.0 +175.8 +198.9 +199.3 +193.4 +193.3 +198.7 +194.5 +180.9 +197.3 +189.7 +193.0 +208.2 +200.1 +193.9 +211.2 +206.6 +210.2 +185.5 +180.8 +206.8 +185.5 +195.8 +199.3 +187.9 +194.9 +175.8 +198.1 +199.0 +200.6 +300.8 +194.0 +199.7 +181.2 +189.9 +195.3 +209.6 +198.1 +184.9 +192.5 +188.8 +193.8 +201.4 +208.2 +192.5 +199.9 +185.0 +207.5 +196.5 +198.8 +193.3 +200.1 +186.7 +194.4 +194.3 +197.2 +198.4 +192.8 +194.3 +188.6 +194.7 +190.7 +192.1 +194.5 +185.7 +194.6 +177.5 +203.6 +180.8 +185.0 +178.9 +205.7 +187.4 +185.9 +192.9 +182.7 +197.3 +198.0 +194.5 +194.7 +194.7 +198.2 +184.7 +199.0 +200.9 +195.4 +198.7 +188.1 +187.5 +190.6 +179.2 +190.2 +195.9 +188.8 +205.7 +191.9 +204.0 +193.3 +199.5 +200.7 +179.3 +190.4 +206.4 +199.8 +189.5 +194.1 +203.3 +196.9 +200.1 +179.6 +217.2 +199.0 +184.0 +177.4 +200.5 +205.3 +193.2 +198.8 +187.2 +191.2 +186.6 +188.3 +199.4 +192.8 +209.8 +181.5 +192.8 +176.0 +189.9 +203.5 +192.5 +193.1 +190.3 +193.1 +203.0 +194.6 +188.4 +199.8 +199.6 +195.0 +200.3 +195.5 +198.5 +203.1 +193.4 +203.6 +195.6 +186.2 +206.4 +197.3 +265.4 +203.7 +205.7 +197.0 +194.9 +193.6 +201.1 +200.6 +197.1 +196.0 +196.3 +195.4 +194.2 +198.4 +202.6 +197.1 +209.4 +204.7 +195.9 +192.8 +203.4 +193.3 +188.5 +190.7 +190.3 +197.6 +197.7 +199.1 +193.0 +198.3 +205.0 +191.4 +197.2 +201.3 +197.6 +197.7 +202.9 +203.4 +198.5 +198.8 +205.4 +194.6 +189.5 +193.3 +190.4 +193.0 +202.5 +198.2 +194.7 +198.5 +184.3 +187.8 +193.3 +190.8 +194.9 +190.3 +201.8 +192.9 +198.5 +195.9 +195.8 +210.1 +194.2 +202.6 +194.5 +197.6 +200.1 +191.8 +192.8 +199.4 +199.7 +199.3 +194.2 +196.9 +195.7 +189.9 +197.1 +205.9 +191.1 +196.5 +200.9 +200.6 +199.1 +203.0 +204.2 +198.7 +192.2 +194.9 +188.6 +194.3 +198.5 +190.6 +189.4 +205.8 +207.0 +200.9 +198.0 +196.9 +196.6 +187.3 +199.9 +196.1 +196.5 +200.0 +186.0 +182.7 +193.3 +195.2 +190.0 +195.9 +190.0 +201.7 +187.1 +199.1 +203.5 +191.8 +199.5 +195.1 +207.3 +205.6 +191.9 +200.1 +196.5 +205.4 +190.8 +195.9 +194.6 +190.2 +197.4 +204.4 +210.5 +289.6 +197.2 +191.4 +199.2 +196.6 +201.4 +191.7 +194.4 +191.9 +193.1 +182.9 +191.5 +202.0 +186.8 +195.5 +193.4 +189.8 +181.3 +199.9 +200.3 +193.5 +196.5 +190.5 +200.6 +209.8 +197.7 +199.5 +200.9 +205.0 +199.4 +206.3 +205.7 +202.7 +189.0 +203.6 +198.9 +188.4 +193.7 +204.1 +198.4 +208.8 +201.4 +198.0 +188.7 +208.9 +196.9 +235.5 +198.1 +202.8 +195.8 +193.2 +203.0 +204.2 +201.2 +201.4 +202.8 +213.8 +197.2 +197.0 +191.2 +196.2 +210.8 +203.5 +193.4 +211.7 +194.0 +204.6 +197.0 +200.0 +197.9 +204.2 +196.6 +184.4 +188.6 +194.4 +188.1 +202.4 +203.0 +204.4 +194.1 +195.8 +195.3 +201.9 +194.7 +205.8 +201.9 +208.6 +195.5 +209.5 +194.8 +202.7 +202.2 +204.4 +197.1 +205.9 +196.2 +197.1 +197.4 +198.6 +198.8 +201.1 +203.9 +200.3 +199.5 +224.5 +199.2 +196.2 +197.7 +194.7 +194.1 +202.5 +191.2 +203.1 +199.1 +197.5 +201.0 +198.7 +207.9 +191.7 +192.1 +191.7 +208.7 +178.3 +202.0 +200.4 +202.1 +206.8 +194.2 +197.0 +203.3 +195.1 +210.0 +193.9 +191.1 +200.1 +192.9 +202.3 +189.5 +193.9 +200.8 +205.5 +198.7 +205.4 +184.7 +198.6 +189.7 +187.8 +202.5 +196.2 +203.5 +213.4 +199.7 +207.5 +207.3 +204.7 +190.5 +194.9 +184.7 +198.0 +200.9 +189.8 +208.9 +189.5 +218.6 +202.8 +189.0 +202.2 +204.3 +191.5 +193.6 +201.6 +204.6 +197.0 +200.9 +186.0 +205.9 +194.1 +203.3 +197.3 +200.3 +195.9 +207.3 +206.7 +206.7 +193.0 +203.1 +238.1 +192.2 +193.3 +197.4 +212.3 +202.5 +197.5 +204.1 +196.6 +183.8 +204.5 +188.1 +217.6 +194.5 +199.1 +210.9 +200.9 +187.7 +199.2 +195.0 +191.0 +198.3 +194.1 +191.9 +213.8 +199.0 +201.8 +197.5 +201.5 +195.6 +207.5 +200.6 +194.2 +210.5 +192.7 +188.1 +203.0 +237.1 +204.7 +205.1 +205.2 +200.3 +188.5 +202.2 +213.1 +195.0 +201.4 +204.2 +195.9 +186.5 +192.2 +206.5 +177.5 +189.9 +192.0 +214.9 +204.0 +194.2 +200.9 +197.1 +200.0 +196.4 +197.2 +189.0 +194.3 +206.5 +192.5 +190.5 +204.1 +196.7 +194.4 +181.9 +193.0 +190.6 +193.6 +178.0 +178.2 +200.9 +189.5 +194.2 +182.1 +183.6 +183.7 +176.9 +181.7 +194.9 +190.7 +187.4 +178.6 +182.0 +186.5 +183.7 +182.1 +186.2 +199.6 +192.4 +189.2 +194.7 +176.3 +184.6 +203.2 +201.9 +195.2 +192.7 +186.7 +195.2 +187.0 +201.1 +202.1 +187.7 +195.9 +181.7 +189.7 +179.9 +177.3 +180.3 +198.3 +184.6 +183.3 +196.9 +178.6 +184.3 +185.3 +183.2 +193.9 +194.7 +195.5 +199.6 +192.0 +189.4 +195.0 +193.6 +200.5 +177.6 +181.0 +200.0 +190.3 +189.7 +205.5 +178.5 +201.7 +192.7 +196.8 +189.2 +177.6 +198.0 +191.8 +178.6 +206.8 +190.0 +192.3 +180.1 +194.6 +179.7 +207.1 +195.6 +200.5 +186.7 +190.1 +178.6 +205.7 +346.2 +188.8 +204.4 +200.7 +176.5 +193.8 +195.9 +193.0 +186.5 +189.5 +190.6 +178.0 +188.6 +186.7 +180.9 +193.8 +194.0 +180.6 +196.7 +178.7 +180.1 +187.7 +179.6 +201.3 +219.2 +184.0 +206.3 +186.7 +192.0 +179.4 +190.1 +187.0 +191.6 +194.7 +195.5 +194.2 +195.8 +200.6 +180.3 +195.6 +209.9 +197.2 +201.1 +196.9 +186.3 +202.7 +182.7 +200.4 +201.2 +196.2 +181.1 +182.6 +187.2 +225.3 +186.8 +197.6 +192.0 +185.0 +199.8 +191.7 +187.4 +192.5 +189.1 +210.9 +187.0 +191.2 +190.9 +207.1 +198.7 +208.8 +190.6 +193.7 +186.5 +182.9 +178.7 +194.1 +184.3 +194.0 +185.4 +215.7 +194.6 +207.9 +204.7 +183.7 +189.3 +196.0 +202.6 +206.2 +190.1 +216.6 +179.8 +206.9 +188.4 +190.8 +181.7 +197.7 +195.7 +178.3 +179.0 +179.3 +203.2 +178.4 +180.1 +175.7 +194.1 +193.0 +203.2 +192.6 +194.0 +190.7 +193.0 +194.2 +183.7 +197.2 +188.4 +176.1 +184.3 +192.2 +195.7 +186.0 +193.7 +196.2 +183.7 +180.7 +181.0 +189.4 +199.3 +201.5 +199.8 +197.2 +201.7 +199.9 +192.2 +188.2 +187.5 +176.6 +205.3 +199.0 +190.0 +202.4 +201.8 +216.8 +208.2 +197.4 +194.3 +205.9 +179.0 +193.5 +182.9 +188.0 +198.3 +197.7 +177.6 +183.8 +182.3 +198.8 +192.5 +183.2 +208.4 +192.4 +193.8 +198.6 +196.2 +183.9 +186.2 +197.8 +180.6 +204.7 +195.3 +182.8 +192.5 +216.3 +203.8 +197.4 +179.5 +188.5 +196.8 +194.4 +189.2 +199.8 +200.7 +205.1 +183.4 +181.5 +193.6 +207.9 +177.4 +191.5 +197.3 +189.8 +191.9 +189.5 +201.2 +211.0 +201.4 +205.5 +201.1 +199.3 +193.5 +203.5 +191.6 +194.0 +196.7 +188.5 +189.8 +195.9 +181.1 +193.2 +197.2 +201.0 +186.2 +200.9 +183.5 +183.0 +207.9 +189.7 +193.8 +182.9 +232.1 +186.2 +200.3 +194.6 +184.2 +192.2 +182.9 +193.6 +207.4 +203.3 +185.6 +197.6 +205.9 +193.0 +182.3 +190.1 +193.3 +200.2 +183.9 +195.7 +186.9 +200.1 +189.6 +194.2 +195.5 +178.3 +183.9 +183.0 +179.2 +209.8 +199.6 +185.4 +210.9 +188.6 +188.9 +185.4 +177.0 +183.0 +186.1 +191.7 +190.7 +192.1 +195.2 +185.0 +186.6 +194.2 +189.5 +187.7 +187.9 +191.8 +181.1 +180.2 +180.0 +185.6 +181.8 +187.9 +192.9 +198.7 +182.0 +182.7 +175.8 +202.0 +190.6 +195.3 +191.0 +201.4 +194.3 +179.5 +185.0 +240.3 +187.1 +197.8 +204.8 +210.7 +203.2 +202.0 +197.8 +203.5 +205.1 +190.0 +190.3 +204.1 +197.9 +198.6 +199.3 +182.1 +200.8 +196.0 +197.1 +203.5 +198.6 +200.7 +201.1 +189.1 +175.8 +199.9 +203.7 +192.2 +182.5 +185.0 +195.9 +204.1 +197.0 +195.5 +202.0 +201.5 +187.1 +186.7 +198.2 +211.8 +197.6 +178.5 +198.5 +196.4 +188.1 +185.0 +199.5 +200.9 +201.7 +193.5 +188.3 +180.9 +190.6 +202.8 +183.9 +190.1 +205.3 +198.6 +183.2 +198.0 +200.9 +198.8 +194.8 +198.3 +195.8 +204.6 +202.6 +207.0 +185.9 +201.9 +195.9 +207.6 +197.4 +206.7 +188.2 +184.7 +183.9 +198.3 +186.4 +191.0 +208.8 +209.4 +187.7 +208.0 +198.4 +191.9 +187.1 +188.5 +189.2 +190.7 +179.9 +204.1 +195.5 +183.7 +183.1 +183.7 +194.6 +187.9 +183.5 +184.7 +203.2 +197.8 +179.4 +185.8 +205.3 +179.8 +194.8 +186.7 +191.5 +197.4 +197.0 +209.2 +194.1 +187.8 +192.9 +202.2 +194.4 +206.6 +191.6 +190.8 +187.5 +193.5 +205.2 +185.1 +185.6 +189.9 +196.0 +203.6 +195.4 +209.9 +196.5 +200.8 +190.1 +191.5 +232.8 +209.2 +193.1 +183.4 +199.5 +187.2 +199.4 +203.6 +198.3 +204.1 +196.4 +202.0 +180.6 +191.6 +202.9 +184.9 +180.8 +192.3 +205.2 +205.5 +179.0 +183.3 +203.1 +184.1 +188.6 +180.9 +195.8 +195.0 +181.1 +198.3 +190.7 +191.8 +192.1 +191.5 +257.4 +197.3 +206.5 +213.4 +184.2 +193.5 +208.6 +209.8 +192.0 +201.5 +198.6 +205.2 +198.9 +212.6 +195.9 +213.7 +215.9 +209.2 +215.2 +207.4 +208.9 +192.2 +192.3 +193.8 +185.1 +206.0 +179.1 +199.9 +198.3 +201.6 +186.8 +202.3 +190.4 +183.6 +191.8 +190.6 +194.7 +184.1 +184.4 +193.8 +194.0 +195.6 +204.5 +194.9 +196.1 +201.1 +196.1 +210.9 +198.7 +192.1 +208.3 +192.8 +193.6 +205.1 +191.5 +190.7 +179.2 +191.6 +192.1 +182.3 +176.1 +185.5 +187.1 +183.4 +191.9 +176.1 +178.0 +185.2 +191.3 +181.3 +178.1 +181.7 +200.3 +187.5 +201.6 +192.3 +177.7 +180.6 +192.9 +185.4 +183.1 +178.4 +196.9 +191.9 +186.1 +195.7 +177.7 +184.1 +195.4 +184.2 +188.8 +179.6 +182.0 +182.9 +185.1 +183.8 +189.3 +216.9 +196.6 +185.7 +235.5 +184.9 +181.8 +180.0 +186.6 +188.4 +190.7 +185.4 +210.9 +184.1 +203.0 +203.5 +198.9 +204.2 +199.7 +182.1 +178.1 +205.1 +179.4 +184.5 +180.7 +200.5 +197.3 +178.2 +178.8 +180.9 +219.2 +180.2 +192.5 +198.6 +238.0 +201.1 +182.4 +203.3 +182.0 +188.9 +201.2 +184.6 +182.7 +187.7 +188.5 +202.5 +199.8 +189.4 +191.1 +181.2 +191.7 +194.6 +181.3 +185.3 +192.8 +182.4 +191.6 +188.3 +202.6 +212.1 +179.6 +185.9 +183.4 +187.8 +184.4 +186.8 +197.0 +191.3 +186.7 +201.9 +187.2 +195.7 +178.5 +187.3 +190.8 +198.4 +198.8 +189.1 +189.6 +197.6 +201.0 +185.0 +182.2 +184.2 +193.2 +191.6 +187.4 +196.3 +190.7 +184.5 +206.1 +200.7 +193.0 +196.2 +195.1 +177.1 +180.7 +187.3 +188.3 +181.5 +180.7 +216.9 +185.7 +196.1 +193.2 +185.5 +186.9 +190.4 +189.7 +196.1 +193.9 +193.6 +185.7 +190.3 +199.8 +190.4 +187.4 +195.5 +190.0 +188.7 +190.2 +195.6 +195.2 +184.7 +186.9 +187.4 +179.3 +184.7 +206.7 +194.3 +198.9 +179.0 +185.7 +185.2 +206.7 +184.3 +202.8 +183.5 +178.9 +238.3 +181.1 +189.5 +176.7 +178.7 +183.4 +180.3 +192.4 +193.6 +186.6 +190.0 +184.0 +188.5 +188.6 +196.3 +182.2 +191.9 +191.1 +197.7 +188.4 +194.2 +201.7 +204.3 +199.6 +190.7 +183.0 +189.9 +192.9 +187.6 +182.8 +203.3 +189.3 +194.0 +189.4 +188.0 +194.4 +211.7 +186.3 +185.3 +187.2 +182.9 +194.9 +187.4 +190.4 +197.5 +184.5 +184.8 +180.0 +197.9 +205.9 +189.2 +196.5 +188.1 +185.4 +195.5 +185.0 +203.1 +188.6 +202.3 +180.6 +191.3 +204.4 +208.2 +189.2 +226.9 +200.0 +178.8 +181.5 +197.7 +204.1 +184.8 +178.5 +186.5 +190.9 +190.4 +181.4 +189.8 +179.2 +189.8 +179.6 +203.8 +205.4 +184.2 +192.8 +204.7 +186.6 +195.5 +203.7 +197.2 +178.0 +180.3 +187.0 +179.1 +187.5 +187.9 +183.4 +181.8 +184.1 +181.3 +184.0 +180.6 +230.8 +193.0 +190.5 +200.4 +187.9 +175.9 +204.2 +206.5 +183.2 +194.2 +187.2 +200.2 +189.6 +188.6 +188.6 +175.4 +184.2 +174.0 +177.2 +180.6 +181.5 +182.3 +187.1 +180.0 +184.7 +179.3 +195.7 +180.5 +179.5 +184.8 +204.0 +202.1 +178.7 +189.4 +196.4 +190.8 +184.3 +189.2 +182.8 +184.6 +178.4 +183.9 +185.0 +184.6 +179.2 +179.0 +182.4 +197.0 +188.4 +188.5 +196.0 +179.6 +188.1 +180.3 +196.1 +189.0 +178.4 +176.5 +186.4 +179.3 +187.7 +187.9 +184.8 +176.9 +188.7 +182.8 +192.3 +190.0 +197.2 +191.6 +197.8 +182.3 +184.2 +187.3 +188.2 +207.3 +189.2 +190.7 +202.3 +191.7 +195.4 +196.2 +190.1 +204.2 +194.8 +187.8 +209.1 +192.1 +193.1 +199.3 +192.1 +204.9 +190.8 +186.5 +189.3 +181.9 +193.3 +182.4 +195.2 +189.1 +196.8 +200.8 +187.6 +201.1 +197.0 +196.6 +177.7 +188.9 +186.2 +186.0 +199.9 +203.8 +190.2 +190.2 +186.3 +179.1 +198.8 +186.3 +192.4 +201.5 +190.2 +191.0 +178.0 +194.1 +197.9 +189.9 +180.2 +176.2 +193.5 +201.9 +201.3 +200.7 +190.3 +198.4 +186.0 +190.2 +182.7 +186.0 +190.0 +196.8 +193.0 +201.5 +184.7 +187.7 +190.8 +187.2 +183.5 +178.1 +178.9 +195.8 +178.2 +188.0 +177.2 +182.5 +184.2 +180.7 +189.6 +203.4 +181.7 +185.1 +177.5 +180.9 +194.0 +190.1 +190.1 +182.9 +184.4 +201.8 +191.1 +184.9 +197.7 +193.3 +190.3 +197.2 +188.2 +191.5 +190.0 +188.0 +187.5 +182.5 +186.6 +183.6 +193.9 +187.5 +186.9 +197.3 +187.2 +177.2 +186.8 +191.9 +186.9 +198.7 +179.7 +191.7 +193.6 +184.2 +178.4 +187.4 +203.3 +196.8 +195.4 +182.9 +187.7 +189.4 +192.5 +190.0 +190.0 +185.4 +194.6 +183.7 +180.2 +199.6 +190.5 +205.9 +176.4 +182.8 +179.0 +199.9 +186.7 +188.8 +189.5 +189.8 +193.5 +203.0 +183.8 +204.2 +176.4 +206.5 +184.7 +174.9 +183.2 +174.4 +196.2 +179.1 +183.7 +180.2 +174.5 +185.5 +203.5 +174.7 +186.1 +175.1 +198.2 +200.3 +184.2 +180.4 +187.9 +194.3 +194.9 +181.3 +180.2 +186.0 +187.6 +180.2 +188.1 +196.5 +175.9 +184.0 +194.4 +186.3 +207.6 +188.1 +185.2 +207.5 +177.7 +204.2 +203.3 +180.8 +176.0 +181.5 +202.3 +191.6 +186.8 +183.0 +189.7 +191.2 +179.2 +196.2 +185.9 +184.3 +183.3 +186.8 +179.3 +188.6 +184.8 +176.7 +189.6 +177.5 +186.3 +197.2 +178.1 +189.8 +175.8 +178.9 +191.9 +176.9 +186.8 +186.6 +194.1 +179.3 +178.7 +183.9 +220.3 +181.6 +183.7 +196.7 +180.2 +176.9 +189.5 +187.5 +185.8 +184.3 +179.9 +186.0 +178.5 +179.5 +227.6 +187.1 +199.2 +183.4 +177.2 +181.3 +190.7 +197.8 +180.1 +203.4 +198.1 +196.8 +184.3 +188.5 +189.6 +187.9 +200.7 +187.1 +193.3 +183.0 +199.1 +187.3 +181.8 +184.0 +178.0 +198.7 +177.4 +195.6 +184.1 +183.4 +186.0 +183.1 +181.8 +190.6 +188.8 +184.8 +196.5 +186.2 +184.7 +182.6 +187.4 +194.1 +185.3 +181.6 +198.4 +190.1 +200.2 +214.9 +195.1 +192.9 +190.8 +179.4 +194.9 +206.0 +193.2 +186.6 +214.1 +216.1 +189.0 +184.2 +201.1 +182.1 +200.2 +188.5 +193.0 +184.8 +188.8 +186.6 +188.3 +193.9 +202.4 +196.8 +214.5 +202.1 +186.7 +197.3 +184.2 +186.5 +202.6 +187.3 +204.3 +176.1 +183.9 +189.2 +189.7 +180.8 +199.4 +185.7 +193.3 +192.7 +192.8 +204.9 +188.7 +196.5 +187.9 +177.4 +189.6 +177.2 +197.3 +183.9 +189.7 +184.6 +191.0 +195.4 +175.7 +179.1 +184.4 +193.2 +180.4 +191.2 +187.2 +181.1 +203.5 +189.2 +186.4 +176.3 +191.5 +193.6 +187.0 +186.8 +187.3 +184.8 +197.6 +207.3 +191.8 +187.1 +196.3 +206.9 +199.9 +186.3 +189.9 +179.8 +200.0 +193.8 +190.9 +180.9 +176.3 +189.1 +180.4 +190.9 +182.4 +182.8 +181.1 +193.9 +184.0 +185.9 +199.1 +199.5 +204.2 +181.9 +199.0 +184.1 +207.4 +194.8 +216.5 +188.4 +206.7 +199.2 +179.3 +186.7 +200.7 +186.3 +195.5 +204.9 +194.0 +187.5 +196.3 +181.4 +186.0 +197.8 +198.1 +196.1 +184.3 +183.3 +211.3 +192.8 +187.9 +183.4 +192.9 +187.4 +194.2 +177.1 +200.0 +199.3 +177.2 +180.1 +178.6 +182.3 +189.1 +191.3 +197.7 +187.6 +187.3 +203.9 +204.4 +180.4 +186.5 +188.4 +176.1 +206.5 +187.7 +194.1 +193.5 +203.7 +186.8 +200.1 +187.0 +203.3 +196.3 +208.1 +192.7 +202.1 +204.7 +183.9 +186.7 +198.2 +189.4 +186.4 +188.3 +195.2 +203.3 +202.3 +201.1 +180.1 +191.3 +180.4 +187.7 +193.4 +196.5 +208.8 +185.3 +186.8 +203.6 +179.0 +185.0 +181.1 +193.8 +196.9 +201.0 +187.3 +189.6 +190.8 +188.9 +197.6 +203.0 +193.2 +185.6 +195.6 +199.1 +191.8 +178.7 +183.6 +184.1 +192.0 +182.4 +183.8 +209.5 +205.7 +187.0 +187.6 +200.6 +187.1 +187.2 +184.9 +180.3 +189.4 +182.7 +196.8 +184.7 +185.8 +184.0 +194.1 +180.5 +199.1 +181.7 +205.3 +182.6 +186.8 +180.2 +181.9 +187.4 +187.9 +191.8 +204.8 +178.5 +181.6 +181.6 +179.5 +181.9 +190.1 +194.1 +180.0 +179.1 +181.2 +175.0 +186.3 +184.8 +182.6 +186.8 +185.3 +181.2 +190.0 +195.3 +186.9 +190.6 +187.3 +193.4 +176.9 +190.5 +194.6 +181.0 +175.8 +187.9 +183.2 +187.7 +191.4 +183.1 +178.6 +193.4 +185.5 +190.1 +194.6 +191.0 +177.7 +187.3 +201.3 +188.6 +182.5 +196.3 +176.2 +184.9 +189.6 +186.9 +227.9 +188.1 +185.0 +182.9 +196.4 +183.1 +179.8 +186.5 +185.2 +199.7 +187.7 +191.6 +191.9 +194.8 +183.9 +185.3 +188.2 +192.7 +180.8 +183.5 +199.8 +196.8 +184.2 +179.5 +204.2 +183.5 +175.4 +207.3 +192.5 +191.9 +208.6 +194.7 +195.2 +190.3 +180.8 +182.6 +203.2 +191.4 +189.7 +183.9 +185.7 +192.3 +190.7 +206.8 +182.9 +195.2 +193.1 +201.2 +177.0 +188.1 +182.3 +185.0 +198.5 +186.4 +183.9 +189.4 +181.8 +199.9 +198.7 +183.7 +191.8 +187.5 +209.4 +201.8 +178.0 +201.8 +198.0 +187.2 +185.5 +178.1 +195.7 +200.1 +174.8 +176.4 +187.6 +177.3 +178.1 +197.5 +183.7 +207.7 +180.6 +176.6 +190.8 +191.0 +180.1 +180.4 +178.8 +185.5 +194.8 +188.1 +185.8 +179.5 +181.0 +196.1 +192.6 +179.2 +180.7 +206.3 +175.9 +196.8 +179.4 +195.1 +187.5 +183.9 +183.8 +184.6 +208.6 +195.5 +202.9 +191.9 +187.0 +189.2 +178.5 +176.1 +186.6 +195.1 +181.6 +201.0 +192.1 +192.9 +202.2 +191.0 +205.8 +204.3 +191.8 +185.1 +190.1 +193.6 +192.4 +185.2 +180.6 +192.6 +185.7 +187.5 +189.2 +184.0 +189.1 +200.6 +185.2 +182.8 +182.0 +185.4 +178.3 +188.4 +193.2 +194.9 +197.1 +194.4 +184.8 +179.2 +195.6 +182.6 +204.3 +203.1 +180.0 +187.7 +187.7 +197.8 +178.7 +186.5 +194.6 +194.8 +176.4 +202.5 +195.4 +180.5 +220.0 +191.0 +217.1 +178.4 +196.1 +182.5 +190.8 +191.7 +202.0 +196.8 +199.4 +183.8 +183.2 +186.5 +178.1 +198.6 +186.1 +195.4 +196.6 +199.3 +196.5 +177.9 +186.3 +180.5 +198.9 +190.2 +188.5 +184.1 +188.6 +201.4 +185.4 +176.4 +182.0 +176.9 +184.4 +179.0 +178.4 +183.8 +190.9 +185.5 +184.4 +179.6 +197.5 +184.3 +179.1 +193.4 +194.2 +179.1 +189.0 +191.2 +180.8 +184.5 +187.9 +191.7 +177.2 +185.4 +183.6 +190.7 +187.5 +188.8 +199.7 +190.0 +176.0 +204.4 +191.1 +188.3 +182.1 +181.6 +191.4 +191.8 +193.1 +186.0 +188.1 +178.4 +193.7 +175.1 +179.0 +186.7 +185.3 +179.7 +182.7 +198.0 +180.2 +189.5 +183.1 +177.1 +182.3 +174.7 +175.6 +186.4 +189.2 +178.9 +180.4 +181.3 +182.3 +180.3 +180.8 +180.1 +197.2 +204.7 +181.6 +182.3 +184.0 +176.5 +214.9 +194.7 +178.3 +175.1 +174.3 +176.5 +190.9 +183.1 +176.8 +177.1 +183.8 +178.5 +189.2 +180.8 +177.6 +175.3 +185.3 +183.4 +175.2 +183.6 +177.0 +181.9 +180.3 +185.1 +179.1 +188.1 +184.4 +181.6 +188.0 +181.6 +190.6 +177.9 +182.7 +177.4 +183.2 +189.3 +187.6 +179.8 +192.3 +188.1 +194.6 +186.7 +181.6 +191.8 +195.6 +187.4 +185.5 +190.0 +184.0 +178.5 +178.0 +200.8 +199.2 +186.5 +174.9 +181.3 +181.0 +187.6 +186.8 +183.3 +183.8 +195.6 +190.6 +193.8 +176.4 +179.1 +182.9 +193.4 +184.4 +186.2 +180.3 +194.7 +182.6 +186.7 +196.4 +196.7 +196.2 +192.3 +189.1 +178.4 +182.3 +195.2 +184.1 +189.0 +184.7 +189.3 +184.1 +177.3 +189.5 +186.1 +186.4 +180.5 +196.1 +187.5 +188.4 +195.8 +192.0 +192.9 +182.4 +199.6 +189.6 +180.2 +176.1 +220.4 +206.6 +195.5 +176.5 +202.2 +187.5 +185.0 +205.6 +195.7 +192.8 +192.8 +174.9 +187.3 +181.9 +180.4 +183.9 +187.6 +192.5 +202.1 +185.2 +200.2 +184.1 +189.9 +194.7 +185.1 +191.9 +176.0 +197.7 +177.5 +178.6 +179.3 +206.0 +193.3 +196.2 +179.3 +181.3 +205.5 +178.1 +186.9 +183.5 +178.5 +193.1 +179.4 +186.1 +174.3 +194.4 +187.1 +195.8 +176.0 +187.6 +176.1 +183.7 +182.0 +183.7 +176.3 +201.0 +185.8 +180.6 +186.1 +180.1 +204.4 +186.1 +182.9 +181.7 +184.6 +177.3 +186.2 +188.7 +180.5 +182.1 +185.3 +177.7 +185.4 +188.2 +194.1 +185.6 +177.8 +192.7 +198.1 +196.0 +187.5 +186.4 +206.8 +207.4 +177.3 +181.3 +185.0 +175.7 +174.2 +191.5 +194.0 +190.8 +199.2 +176.4 +202.7 +193.1 +182.6 +186.0 +196.0 +186.1 +189.4 +203.4 +184.8 +194.3 +187.2 +183.5 +199.4 +196.7 +180.2 +195.2 +180.9 +182.7 +193.5 +200.0 +193.3 +184.4 +199.1 +206.2 +199.0 +195.8 +189.4 +194.1 +196.4 +201.5 +180.7 +189.7 +193.3 +200.3 +205.5 +189.9 +192.6 +187.1 +205.7 +194.9 +188.0 +189.2 +197.9 +188.5 +185.0 +182.7 +205.8 +186.7 +189.2 +185.1 +182.4 +205.1 +181.4 +185.6 +188.5 +195.9 +199.8 +185.4 +194.7 +201.2 +185.0 +202.1 +200.0 +181.9 +186.8 +195.9 +182.2 +186.6 +187.1 +184.9 +181.1 +190.1 +191.0 +186.7 +184.6 +181.8 +190.4 +185.7 +183.4 +187.0 +182.6 +183.5 +173.9 +181.8 +194.1 +182.2 +182.6 +181.8 +187.0 +192.2 +178.9 +206.9 +197.1 +180.7 +183.4 +176.7 +187.9 +194.5 +182.0 +182.0 +190.9 +177.9 +180.3 +190.8 +187.7 +179.1 +187.9 +183.7 +187.9 +183.5 +175.9 +186.5 +178.2 +180.7 +194.3 +184.8 +181.4 +182.8 +179.8 +187.0 +179.1 +178.8 +187.2 +185.9 +182.0 +187.7 +181.3 +186.0 +181.0 +183.2 +186.5 +189.3 +187.8 +182.8 +179.4 +199.0 +180.9 +193.8 +202.6 +185.8 +177.5 +182.7 +202.4 +213.8 +190.6 +189.8 +190.5 +185.0 +183.7 +191.0 +185.7 +182.2 +194.8 +183.1 +191.8 +186.6 +185.2 +181.7 +182.1 +185.7 +195.0 +181.5 +185.1 +181.5 +187.3 +186.5 +188.7 +185.2 +180.7 +178.3 +191.8 +185.4 +188.6 +184.5 +187.5 +191.5 +191.5 +195.8 +197.9 +194.2 +184.9 +182.7 +182.1 +185.6 +188.4 +178.8 +190.4 +179.3 +186.2 +206.0 +191.2 +195.1 +198.4 +189.9 +180.8 +181.1 +198.5 +203.4 +190.8 +177.1 +184.0 +181.0 +192.2 +175.9 +200.4 +192.2 +181.1 +202.0 +198.6 +203.6 +191.7 +193.4 +192.6 +186.5 +182.4 +195.8 +184.9 +191.0 +176.7 +182.7 +194.8 +181.9 +191.1 +205.5 +176.3 +200.1 +184.1 +208.8 +184.3 +196.2 +190.2 +197.1 +186.8 +181.7 +188.5 +188.6 +187.2 +195.3 +198.6 +205.7 +185.4 +187.5 +176.2 +186.1 +199.4 +294.6 +232.7 +200.5 +181.8 +189.1 +186.9 +190.7 +179.7 +181.3 +181.5 +200.5 +181.2 +181.7 +185.4 +195.5 +201.9 +199.7 +194.2 +179.6 +181.4 +205.5 +183.1 +499.4 +188.5 +184.5 +194.5 +195.5 +193.6 +189.0 +196.4 +188.3 +186.7 +190.6 +176.9 +188.1 +189.4 +198.2 +178.8 +196.5 +203.1 +192.3 +193.8 +188.4 +180.2 +192.3 +187.1 +195.2 +181.6 +185.5 +190.9 +185.2 +200.5 +190.0 +180.2 +194.4 +196.5 +187.7 +184.3 +189.5 +188.2 +189.5 +194.5 +192.4 +202.1 +177.9 +193.8 +191.7 +182.2 +190.1 +197.4 +196.6 +193.7 +195.9 +200.9 +178.8 +181.4 +193.6 +193.5 +177.7 +192.9 +190.9 +201.2 +184.9 +175.8 +197.9 +202.4 +192.3 +202.5 +198.3 +196.8 +185.2 +184.8 +198.1 +191.3 +174.0 +180.5 +193.4 +183.0 +185.1 +177.0 +190.4 +181.3 +179.0 +180.6 +179.1 +188.8 +207.2 +183.7 +186.6 +198.3 +176.6 +186.5 +187.4 +179.5 +189.2 +197.7 +181.5 +194.6 +195.5 +181.6 +197.3 +190.7 +202.0 +183.9 +193.0 +183.9 +196.9 +203.5 +189.4 +187.1 +185.2 +184.8 +199.8 +175.7 +180.6 +187.6 +185.4 +178.5 +185.5 +178.7 +187.5 +185.7 +191.9 +180.2 +186.7 +183.0 +196.7 +197.6 +197.1 +187.4 +194.8 +194.7 +191.0 +185.1 +187.0 +185.8 +197.6 +204.2 +187.9 +189.9 +217.5 +202.8 +192.4 +176.4 +178.8 +199.8 +192.5 +199.9 +190.8 +202.8 +187.0 +180.2 +196.6 +176.5 +187.6 +181.3 +191.0 +180.6 +184.7 +195.8 +191.5 +182.7 +181.3 +189.3 +182.5 +184.2 +187.9 +202.4 +195.7 +189.5 +195.4 +201.2 +186.1 +208.8 +199.6 +189.4 +180.5 +194.2 +184.5 +187.5 +196.6 +188.0 +210.1 +193.3 +187.6 +191.6 +188.5 +179.2 +192.5 +189.7 +181.6 +180.9 +187.9 +180.6 +181.1 +178.7 +175.7 +176.2 +187.1 +185.0 +186.9 +186.5 +190.0 +185.6 +201.2 +189.3 +189.3 +189.7 +188.2 +187.2 +175.0 +185.5 +185.0 +205.2 +187.3 +177.2 +179.2 +190.2 +183.0 +192.9 +183.2 +185.4 +179.2 +186.2 +183.1 +183.8 +186.6 +185.7 +192.4 +179.6 +196.2 +180.2 +185.9 +197.4 +192.2 +177.5 +180.7 +190.2 +176.9 +193.6 +186.2 +184.9 +182.0 +186.1 +187.4 +184.3 +196.2 +203.1 +180.6 +192.5 +196.2 +195.9 +181.6 +189.9 +183.3 +192.7 +187.3 +191.0 +200.2 +180.1 +187.0 +175.7 +179.9 +193.1 +190.2 +202.1 +174.1 +179.5 +185.1 +182.7 +196.7 +179.8 +186.7 +192.5 +189.2 +193.7 +184.1 +185.6 +184.6 +193.6 +181.0 +189.2 +179.9 +181.4 +183.7 +184.5 +193.1 +191.5 +182.9 +190.2 +185.2 +185.9 +216.6 +184.1 +195.7 +181.2 +182.8 +199.3 +187.4 +184.9 +177.6 +206.2 +184.1 +201.5 +200.7 +192.2 +187.0 +182.1 +196.6 +181.6 +189.2 +191.6 +188.8 +186.9 +193.6 +184.2 +191.3 +194.9 +194.6 +188.7 +179.2 +205.1 +181.0 +177.3 +183.7 +184.8 +183.7 +204.3 +190.7 +181.6 +191.6 +189.5 +195.5 +176.0 +193.0 +192.1 +195.9 +205.8 +192.2 +190.6 +198.3 +185.8 +186.7 +198.6 +184.2 +203.1 +199.6 +187.0 +204.3 +207.2 +190.8 +196.7 +188.8 +193.2 +188.7 +199.8 +204.3 +188.6 +192.0 +209.8 +180.3 +203.7 +196.4 +185.7 +182.1 +193.3 +200.9 +196.6 +181.9 +182.2 +179.2 +197.3 +189.3 +200.9 +184.5 +185.8 +185.6 +187.5 +187.5 +189.5 +186.9 +187.2 +195.7 +182.0 +179.0 +191.4 +188.5 +177.7 +185.7 +182.0 +189.0 +198.7 +188.4 +186.6 +180.4 +185.1 +184.2 +207.6 +184.2 +190.0 +181.5 +190.9 +191.2 +187.8 +177.8 +182.7 +191.8 +206.2 +201.4 +182.2 +189.2 +195.4 +187.9 +195.7 +190.4 +183.3 +183.2 +178.2 +189.0 +179.2 +175.5 +187.3 +181.1 +180.1 +188.1 +184.4 +183.9 +200.2 +185.4 +180.4 +184.0 +183.8 +181.2 +203.6 +201.1 +197.1 +192.3 +182.7 +182.4 +183.0 +177.3 +181.3 +185.3 +184.6 +188.7 +179.1 +179.1 +184.6 +176.0 +177.8 +186.9 +189.8 +180.3 +177.2 +184.2 +179.2 +177.4 +182.0 +186.6 +188.7 +194.2 +184.0 +178.9 +175.3 +191.6 +182.9 +188.3 +178.7 +201.6 +186.4 +180.8 +184.7 +186.7 +196.9 +177.7 +178.8 +175.3 +182.7 +184.0 +182.4 +196.6 +174.7 +178.0 +181.5 +198.1 +185.1 +177.5 +181.2 +188.1 +196.0 +183.6 +196.2 +185.9 +193.2 +186.7 +179.7 +195.3 +190.1 +206.8 +186.2 +178.5 +192.0 +187.6 +176.9 +191.8 +190.8 +193.6 +200.0 +192.3 +178.0 +184.6 +179.4 +178.2 +183.2 +179.9 +184.9 +183.7 +189.1 +176.4 +185.5 +184.8 +192.5 +185.3 +193.9 +179.6 +181.7 +179.1 +210.8 +185.6 +184.2 +181.3 +198.8 +180.2 +182.5 +191.9 +196.4 +186.6 +185.3 +182.5 +175.8 +182.0 +186.9 +186.6 +176.0 +175.4 +186.5 +175.4 +182.4 +199.5 +192.7 +182.0 +199.9 +181.1 +196.7 +186.2 +182.0 +175.9 +184.4 +183.8 +178.4 +191.3 +177.8 +196.1 +200.8 +200.6 +191.1 +190.4 +180.3 +200.6 +201.4 +181.3 +176.9 +183.2 +185.0 +178.4 +187.8 +172.7 +182.8 +186.3 +193.9 +179.2 +194.9 +176.8 +188.1 +190.7 +192.3 +178.4 +191.9 +196.6 +183.5 +197.8 +192.0 +176.0 +192.1 +191.5 +193.3 +209.5 +185.2 +187.6 +186.3 +190.9 +183.9 +190.2 +186.3 +184.7 +177.3 +185.0 +178.3 +193.0 +181.7 +190.9 +185.3 +174.9 +192.3 +194.5 +194.1 +177.0 +185.2 +193.2 +202.4 +202.0 +197.2 +199.3 +176.9 +185.7 +188.1 +186.8 +184.4 +204.5 +196.4 +181.3 +190.9 +177.2 +192.4 +179.9 +177.0 +194.1 +203.5 +189.1 +194.7 +181.5 +183.6 +199.8 +187.3 +185.1 +195.7 +177.8 +178.3 +189.8 +200.1 +176.9 +177.9 +188.9 +190.9 +198.7 +197.7 +186.9 +190.8 +201.4 +188.5 +180.4 +196.2 +185.8 +194.9 +193.3 +181.3 +195.4 +197.5 +201.3 +179.5 +177.7 +201.9 +199.9 +183.5 +177.6 +190.4 +183.7 +178.4 +203.6 +195.9 +199.5 +193.2 +175.0 +184.5 +190.6 +194.3 +178.2 +177.5 +188.4 +179.7 +183.8 +174.2 +186.5 +179.8 +196.9 +194.9 +178.4 +185.7 +190.1 +181.3 +180.0 +179.9 +181.7 +187.4 +185.9 +178.9 +184.6 +189.5 +186.6 +185.8 +188.4 +188.3 +174.7 +184.9 +192.5 +185.3 +174.9 +180.6 +179.5 +183.4 +177.7 +177.5 +187.4 +183.7 +188.0 +177.6 +180.7 +180.9 +181.9 +188.9 +201.1 +186.6 +176.3 +177.3 +177.6 +175.6 +201.5 +175.5 +188.2 +181.8 +176.9 +185.3 +179.6 +192.7 +203.8 +183.4 +179.4 +177.9 +182.8 +182.3 +174.9 +180.5 +180.2 +187.1 +190.4 +185.5 +184.6 +185.4 +190.3 +183.9 +180.0 +201.8 +198.1 +184.2 +192.8 +184.2 +195.7 +191.4 +188.8 +192.9 +184.8 +174.3 +191.8 +185.4 +183.8 +192.7 +192.6 +194.8 +177.9 +178.7 +181.5 +177.3 +192.5 +176.0 +178.9 +199.7 +181.0 +178.8 +177.1 +183.0 +176.7 +182.3 +180.9 +183.1 +183.9 +174.2 +178.7 +173.8 +174.4 +179.1 +178.4 +175.4 +181.4 +180.9 +178.2 +182.5 +184.2 +183.7 +182.6 +176.2 +177.2 +180.5 +183.9 +193.4 +174.9 +178.8 +176.0 +178.1 +176.1 +177.3 +178.7 +182.2 +191.0 +186.8 +188.4 +180.3 +177.8 +198.8 +176.7 +198.3 +178.5 +177.9 +179.9 +186.7 +178.0 +175.4 +186.6 +190.8 +190.7 +183.5 +196.3 +182.1 +184.4 +188.8 +186.3 +184.8 +191.3 +186.2 +191.0 +188.3 +178.8 +176.3 +181.2 +183.1 +180.4 +178.6 +176.2 +176.1 +177.5 +180.6 +178.1 +176.4 +195.4 +178.8 +179.6 +178.1 +179.7 +178.3 +181.9 +177.4 +181.4 +186.7 +174.6 +179.4 +177.5 +197.6 +196.5 +193.1 +179.2 +182.2 +178.1 +186.3 +182.1 +175.4 +177.5 +188.1 +182.9 +179.8 +179.2 +180.0 +176.9 +177.4 +192.5 +177.5 +177.4 +181.8 +175.6 +178.9 +180.6 +176.8 +182.8 +186.7 +182.5 +188.3 +183.7 +185.1 +195.4 +179.2 +180.7 +190.0 +187.5 +180.3 +183.0 +184.2 +184.3 +189.4 +180.1 +179.6 +182.3 +177.6 +186.3 +188.0 +182.1 +187.1 +186.2 +184.2 +202.3 +187.0 +188.0 +180.5 +194.6 +188.8 +190.7 +181.9 +182.1 +185.3 +183.1 +197.3 +195.0 +208.3 +195.7 +179.1 +196.3 +185.5 +185.4 +186.9 +193.9 +187.1 +184.2 +180.0 +184.8 +189.6 +192.3 +185.6 +185.8 +185.9 +187.9 +186.2 +177.3 +192.2 +201.9 +185.9 +181.7 +192.8 +185.4 +187.3 +187.5 +178.9 +187.6 +181.3 +186.9 +179.6 +179.3 +189.4 +177.5 +184.5 +176.4 +193.3 +178.7 +191.6 +182.7 +194.6 +187.4 +190.4 +199.1 +191.6 +193.1 +187.0 +181.7 +180.3 +196.6 +180.5 +180.0 +184.9 +176.9 +176.3 +179.3 +189.1 +180.4 +176.0 +174.9 +181.2 +177.6 +177.4 +177.6 +179.0 +178.7 +178.7 +184.8 +177.9 +179.4 +183.8 +181.9 +177.1 +175.8 +182.9 +183.2 +176.8 +181.7 +183.7 +192.1 +178.2 +181.4 +180.0 +176.8 +179.8 +194.3 +181.6 +180.7 +177.5 +187.4 +179.0 +180.0 +186.0 +182.0 +175.4 +178.9 +181.9 +192.0 +179.5 +178.5 +175.3 +184.1 +178.3 +177.1 +186.2 +196.9 +182.0 +178.0 +181.4 +181.4 +179.6 +180.2 +180.4 +183.1 +176.6 +182.9 +176.3 +191.5 +179.9 +178.1 +178.4 +189.3 +196.8 +188.2 +180.5 +185.2 +188.0 +176.3 +181.0 +187.4 +178.9 +179.1 +185.3 +183.4 +185.8 +178.5 +176.1 +188.9 +200.9 +193.9 +193.4 +184.0 +187.2 +178.1 +193.5 +182.0 +180.1 +182.8 +176.8 +177.0 +184.0 +185.2 +197.3 +180.8 +190.4 +177.3 +183.4 +191.3 +175.4 +176.8 +192.3 +177.3 +191.1 +185.1 +179.7 +190.8 +175.9 +181.2 +194.0 +176.5 +180.3 +175.1 +185.1 +181.3 +200.0 +187.0 +182.6 +182.0 +176.8 +190.1 +182.1 +181.7 +192.3 +177.0 +185.1 +181.3 +179.2 +182.1 +176.6 +185.0 +177.4 +183.5 +196.5 +184.2 +179.2 +180.5 +180.8 +181.9 +176.9 +178.6 +175.9 +192.0 +198.7 +189.6 +190.5 +186.3 +192.6 +196.0 +176.2 +175.9 +176.5 +195.4 +174.8 +177.4 +186.7 +174.9 +176.1 +183.0 +178.1 +177.4 +192.9 +179.7 +198.5 +178.1 +178.6 +195.4 +181.6 +179.9 +179.5 +186.4 +180.6 +180.3 +187.3 +193.9 +192.5 +178.1 +184.8 +194.9 +194.6 +181.4 +177.6 +180.4 +175.5 +180.5 +177.2 +186.6 +176.8 +176.3 +186.0 +179.8 +178.2 +177.9 +178.9 +190.1 +180.8 +180.1 +181.3 +193.5 +185.0 +198.7 +199.9 +183.4 +177.3 +176.2 +182.7 +178.2 +175.2 +177.7 +186.9 +182.1 +195.1 +181.3 +182.4 +175.7 +185.5 +180.8 +181.4 +181.6 +190.1 +183.7 +186.4 +194.3 +176.4 +176.7 +189.9 +175.6 +206.6 +178.9 +180.0 +178.8 +180.1 +184.1 +176.3 +177.9 +194.3 +188.5 +182.6 +178.6 +177.5 +190.9 +178.9 +180.3 +193.9 +187.4 +186.4 +190.1 +184.7 +199.2 +187.9 +177.7 +180.5 +189.4 +176.5 +192.8 +181.4 +178.0 +193.6 +175.8 +184.4 +184.1 +191.9 +176.2 +184.5 +180.4 +185.1 +180.0 +192.6 +187.6 +190.2 +183.0 +176.5 +194.4 +178.4 +184.5 +176.6 +179.0 +182.6 +187.2 +180.4 +183.3 +180.8 +179.1 +193.9 +181.9 +188.8 +180.0 +175.2 +194.5 +194.8 +187.7 +182.5 +199.3 +175.0 +175.9 +177.9 +179.6 +179.4 +176.0 +176.4 +175.8 +177.2 +176.8 +179.0 +180.4 +177.1 +174.9 +177.9 +180.6 +187.3 +176.6 +177.3 +184.4 +179.2 +177.2 +175.7 +183.3 +177.4 +180.7 +175.6 +173.6 +179.6 +177.2 +180.8 +176.4 +187.7 +178.4 +176.0 +192.5 +181.4 +183.5 +178.5 +186.6 +179.4 +180.1 +181.6 +178.3 +178.2 +184.1 +180.6 +178.3 +184.1 +181.6 +177.4 +183.4 +180.6 +178.8 +179.0 +175.1 +177.0 +180.8 +179.9 +177.7 +176.3 +177.3 +184.2 +176.9 +190.8 +183.0 +181.0 +184.0 +178.6 +175.2 +177.2 +177.9 +183.2 +180.9 +176.3 +177.0 +182.8 +178.5 +176.9 +178.1 +175.5 +184.7 +176.6 +177.2 +182.4 +189.5 +197.3 +179.9 +190.4 +182.4 +188.7 +185.5 +181.1 +182.6 +187.0 +184.4 +179.1 +180.1 +180.9 +187.2 +175.9 +194.1 +177.9 +177.4 +200.7 +201.4 +183.5 +180.8 +195.5 +181.0 +180.2 +181.5 +175.9 +175.3 +180.1 +182.1 +180.1 +176.5 +178.9 +200.5 +178.6 +176.6 +175.8 +175.8 +176.0 +199.3 +177.5 +180.9 +185.1 +200.3 +194.0 +177.7 +181.2 +182.1 +185.1 +183.4 +180.7 +188.8 +187.3 +179.1 +177.5 +185.5 +185.5 +177.2 +175.3 +178.6 +184.5 +180.0 +179.9 +177.1 +178.0 +187.8 +183.6 +189.6 +181.5 +179.7 +179.0 +188.8 +185.1 +177.0 +177.1 +176.3 +176.4 +199.6 +178.1 +188.2 +176.9 +191.5 +194.0 +182.6 +184.0 +186.4 +184.1 +180.0 +181.1 +187.3 +176.1 +174.5 +189.3 +188.9 +178.7 +177.2 +191.0 +194.3 +187.7 +177.0 +176.4 +183.3 +176.2 +181.3 +181.0 +176.8 +187.8 +178.2 +202.3 +191.6 +180.3 +174.8 +187.8 +176.9 +176.9 +181.1 +177.5 +186.1 +179.3 +187.2 +176.6 +178.6 +178.7 +179.0 +188.7 +188.5 +183.8 +192.2 +178.8 +180.4 +182.1 +177.0 +175.1 +180.1 +176.7 +176.0 +188.1 +191.3 +186.6 +202.0 +182.5 +175.9 +182.1 +195.6 +174.8 +194.5 +181.2 +191.8 +180.4 +178.7 +186.1 +180.9 +186.4 +177.0 +183.6 +179.7 +208.1 +179.4 +179.1 +178.5 +179.2 +180.7 +183.3 +180.4 +187.6 +176.9 +185.4 +187.5 +184.3 +183.5 +181.0 +190.8 +178.6 +180.0 +180.4 +191.0 +182.8 +186.9 +179.6 +189.1 +186.9 +202.3 +181.1 +192.1 +194.3 +176.7 +185.8 +177.3 +179.5 +177.2 +177.8 +190.5 +190.1 +184.1 +191.8 +178.7 +185.9 +178.3 +177.4 +176.4 +177.8 +179.0 +177.6 +179.7 +178.1 +190.5 +177.8 +180.4 +181.6 +181.3 +177.3 +182.1 +189.7 +178.2 +179.7 +181.3 +178.8 +194.0 +200.6 +179.6 +177.9 +181.0 +184.7 +180.8 +181.1 +187.8 +183.4 +186.2 +181.8 +175.8 +176.8 +176.3 +177.0 +186.7 +178.5 +183.0 +177.1 +182.3 +182.2 +180.6 +180.6 +185.2 +178.9 +192.3 +177.6 +212.1 +180.5 +186.4 +187.4 +196.4 +181.9 +194.5 +188.5 +179.4 +179.3 +181.4 +185.7 +176.9 +182.6 +185.0 +181.7 +178.9 +184.4 +180.4 +189.2 +187.5 +204.6 +189.7 +182.6 +182.1 +180.0 +182.3 +180.1 +189.3 +178.1 +183.8 +180.9 +184.8 +182.2 +177.7 +176.1 +197.5 +195.8 +183.3 +183.4 +199.0 +205.4 +183.6 +182.5 +182.2 +199.1 +187.1 +179.5 +182.9 +180.2 +177.5 +183.4 +189.4 +196.2 +181.2 +183.3 +178.6 +181.5 +185.5 +179.8 +184.3 +198.8 +184.9 +178.0 +177.7 +181.0 +186.0 +196.3 +178.1 +192.4 +178.7 +181.4 +192.5 +181.4 +192.5 +182.2 +178.9 +183.9 +185.3 +192.2 +186.0 +193.4 +178.4 +194.5 +179.9 +187.6 +185.9 +179.2 +185.0 +188.1 +189.4 +187.9 +190.0 +191.9 +187.5 +186.0 +189.6 +182.4 +185.6 +187.0 +192.4 +183.3 +189.2 +194.6 +195.9 +190.7 +186.4 +185.9 +185.3 +177.1 +184.6 +186.4 +183.1 +186.2 +180.0 +181.8 +184.7 +176.6 +178.0 +178.5 +182.2 +200.3 +184.2 +186.9 +178.2 +190.3 +194.9 +193.1 +193.5 +188.5 +177.9 +185.4 +177.6 +179.7 +193.0 +200.3 +182.0 +192.3 +183.3 +183.6 +183.2 +190.8 +182.6 +177.7 +177.1 +183.7 +177.5 +175.4 +180.3 +181.3 +178.8 +182.3 +176.5 +177.4 +176.0 +178.8 +184.3 +183.0 +180.2 +184.9 +188.1 +187.6 +182.8 +183.0 +180.0 +177.5 +196.1 +185.1 +175.5 +175.9 +178.6 +201.5 +177.1 +191.7 +184.8 +176.2 +184.4 +175.9 +182.6 +182.0 +180.8 +178.7 +191.3 +179.5 +178.7 +174.8 +185.1 +174.9 +189.8 +191.3 +191.9 +190.7 +175.8 +177.3 +183.6 +178.0 +185.1 +177.2 +178.6 +188.7 +179.0 +186.0 +202.6 +177.2 +179.8 +196.0 +195.8 +186.8 +183.6 +202.2 +182.0 +175.8 +186.6 +178.2 +181.3 +180.8 +183.6 +181.9 +192.4 +190.8 +179.2 +184.6 +186.2 +183.0 +196.5 +179.6 +183.4 +187.2 +179.5 +185.3 +182.2 +177.8 +176.6 +177.2 +179.3 +177.1 +181.9 +178.9 +180.2 +179.8 +175.0 +176.8 +180.5 +175.7 +176.3 +180.6 +181.6 +176.6 +175.4 +177.4 +176.2 +180.8 +188.0 +178.2 +205.6 +177.8 +176.5 +179.4 +176.4 +183.3 +177.9 +174.2 +176.1 +182.8 +180.8 +186.9 +179.4 +195.8 +178.8 +181.6 +199.7 +175.8 +186.7 +179.1 +182.4 +179.1 +176.3 +180.5 +174.7 +180.8 +178.2 +185.2 +183.3 +192.7 +184.9 +205.8 +177.0 +182.5 +187.3 +185.9 +184.3 +177.0 +193.2 +179.0 +177.5 +181.3 +176.2 +178.8 +197.3 +180.8 +180.8 +189.7 +188.3 +179.5 +179.3 +185.3 +184.8 +192.3 +180.4 +186.7 +180.0 +178.9 +177.9 +179.4 +177.3 +181.9 +175.4 +174.1 +180.2 +176.9 +178.6 +177.5 +176.4 +177.7 +180.6 +182.2 +178.0 +179.3 +176.9 +188.6 +180.4 +179.8 +180.7 +177.2 +176.7 +175.3 +176.4 +178.4 +177.8 +180.1 +193.5 +181.0 +183.6 +177.9 +181.9 +186.9 +197.3 +175.7 +177.6 +184.7 +179.6 +182.2 +181.9 +181.2 +176.7 +185.5 +196.8 +185.9 +182.4 +178.1 +180.2 +178.3 +181.7 +186.4 +175.6 +182.6 +179.6 +181.0 +188.0 +178.1 +176.9 +181.0 +179.6 +185.0 +177.7 +179.4 +178.0 +185.7 +179.0 +194.2 +175.4 +178.5 +175.0 +176.7 +179.3 +188.8 +181.0 +177.3 +188.0 +178.5 +180.0 +175.9 +180.7 +181.2 +177.0 +179.2 +179.3 +193.1 +176.0 +179.4 +185.4 +177.4 +177.2 +177.9 +176.4 +186.7 +178.0 +178.7 +180.5 +186.7 +176.4 +185.8 +182.5 +177.3 +180.3 +179.5 +174.6 +184.2 +184.0 +176.3 +176.0 +211.4 +184.5 +182.1 +180.5 +188.1 +177.9 +179.7 +177.5 +184.5 +180.6 +191.1 +187.4 +176.3 +180.4 +183.2 +184.8 +178.6 +189.8 +180.9 +176.6 +176.9 +176.4 +182.0 +178.2 +177.3 +189.9 +191.1 +178.0 +179.5 +176.7 +194.2 +211.7 +175.9 +177.9 +176.0 +179.7 +180.6 +176.4 +180.3 +177.2 +183.6 +180.6 +187.4 +189.4 +189.6 +177.1 +179.4 +184.3 +186.4 +181.9 +180.1 +184.8 +182.0 +188.2 +181.4 +184.8 +177.5 +180.3 +195.4 +185.7 +176.8 +176.0 +187.3 +202.2 +194.5 +181.1 +182.9 +197.6 +186.9 +181.3 +180.9 +183.9 +191.0 +182.5 +186.9 +179.7 +179.2 +185.6 +184.3 +180.4 +190.9 +183.6 +183.1 +175.0 +180.9 +186.3 +174.8 +176.4 +176.1 +175.7 +178.1 +178.0 +180.5 +182.6 +180.0 +179.2 +186.8 +188.3 +179.8 +179.6 +180.5 +176.9 +204.2 +195.2 +180.2 +177.9 +187.9 +191.3 +183.9 +185.3 +186.8 +182.5 +189.5 +176.8 +180.5 +189.3 +185.9 +178.0 +176.0 +179.2 +181.3 +175.6 +179.9 +179.4 +183.8 +178.8 +177.9 +180.1 +182.3 +180.9 +175.7 +178.1 +173.7 +177.9 +180.0 +176.2 +177.7 +179.4 +192.3 +175.7 +185.7 +188.6 +195.5 +182.5 +184.1 +177.9 +177.8 +183.0 +174.9 +174.9 +179.7 +186.4 +185.9 +175.2 +179.2 +176.8 +181.5 +178.3 +180.6 +176.8 +175.8 +175.9 +180.3 +179.7 +175.8 +174.1 +180.6 +176.7 +179.7 +175.7 +179.2 +178.3 +203.4 +177.9 +186.9 +175.7 +177.0 +178.8 +180.2 +180.1 +185.5 +175.0 +178.6 +183.3 +177.2 +178.3 +180.1 +175.9 +175.6 +179.3 +179.8 +178.0 +186.5 +176.4 +187.2 +181.6 +175.4 +177.5 +179.1 +179.2 +175.9 +174.3 +175.5 +177.4 +178.5 +182.0 +181.4 +184.4 +183.4 +174.4 +177.5 +175.0 +175.4 +174.1 +182.8 +184.9 +178.3 +182.9 +182.3 +186.6 +180.9 +191.6 +185.6 +184.8 +190.2 +175.8 +185.2 +175.5 +176.9 +176.7 +176.9 +176.5 +179.2 +179.9 +179.2 +177.8 +188.9 +180.3 +186.8 +182.0 +180.8 +176.6 +180.5 +175.3 +185.0 +181.1 +181.1 +176.5 +176.5 +178.0 +175.9 +180.2 +178.3 +188.0 +185.9 +178.6 +179.5 +187.6 +180.2 +181.3 +182.5 +182.8 +175.0 +184.9 +181.7 +178.5 +177.5 +177.1 +176.8 +179.0 +179.2 +175.5 +177.1 +178.9 +174.8 +179.0 +177.3 +179.2 +178.2 +177.5 +191.8 +177.6 +182.1 +178.7 +177.2 +182.5 +181.5 +185.6 +179.5 +183.9 +177.6 +179.6 +193.9 +175.9 +216.0 +180.4 +177.3 +178.8 +176.7 +177.3 +178.3 +188.8 +175.8 +175.7 +176.0 +178.3 +185.1 +179.0 +191.3 +180.0 +178.8 +189.8 +182.7 +176.7 +178.0 +184.2 +185.0 +190.3 +178.0 +178.7 +177.4 +186.0 +176.4 +179.6 +178.0 +182.2 +175.9 +175.7 +178.3 +174.7 +193.6 +177.2 +186.6 +183.7 +181.0 +192.1 +176.4 +188.6 +205.9 +199.0 +194.6 +193.4 +190.4 +178.1 +188.0 +183.8 +180.5 +180.5 +190.5 +194.5 +180.8 +185.3 +193.4 +183.1 +178.1 +197.4 +190.2 +181.0 +184.9 +189.0 +184.6 +183.7 +187.7 +187.9 +182.5 +192.6 +184.3 +178.7 +179.8 +196.8 +184.4 +195.5 +183.9 +186.6 +181.0 +188.7 +185.0 +197.1 +204.2 +176.8 +179.6 +185.3 +182.1 +178.6 +180.6 +183.0 +199.1 +180.5 +180.7 +189.2 +194.5 +187.1 +192.3 +179.3 +196.8 +197.3 +180.3 +177.0 +178.8 +183.2 +183.1 +176.6 +201.6 +183.8 +182.7 +177.0 +185.2 +180.1 +199.4 +203.2 +187.4 +185.7 +184.1 +183.0 +198.7 +186.3 +187.9 +199.4 +185.9 +190.3 +202.8 +204.6 +178.1 +187.6 +196.5 +180.3 +181.7 +182.9 +199.2 +198.8 +190.5 +219.6 +180.0 +203.5 +182.0 +184.1 +176.8 +200.9 +201.9 +180.9 +177.9 +183.4 +179.5 +197.8 +188.0 +204.1 +185.6 +179.3 +190.6 +184.3 +195.2 +188.0 +200.9 +187.7 +183.0 +180.9 +180.0 +184.7 +176.8 +181.9 +180.6 +180.0 +177.7 +182.2 +175.2 +182.9 +186.3 +203.4 +183.3 +175.8 +186.2 +179.8 +178.4 +178.2 +420.1 +177.9 +193.8 +183.8 +180.0 +188.8 +187.2 +192.0 +180.8 +182.3 +191.8 +183.2 +176.6 +179.5 +215.2 +193.8 +188.3 +180.3 +186.4 +181.8 +205.8 +175.1 +186.5 +205.9 +185.5 +187.1 +191.7 +189.4 +188.3 +180.1 +179.8 +178.9 +179.3 +181.9 +184.9 +188.4 +176.2 +177.0 +181.8 +179.5 +177.3 +176.0 +208.3 +185.6 +180.6 +180.3 +195.0 +177.7 +175.7 +214.3 +188.2 +177.7 +187.1 +197.1 +184.8 +178.2 +182.1 +178.4 +176.3 +190.6 +187.3 +177.8 +177.2 +191.7 +200.2 +188.4 +177.3 +177.0 +178.5 +180.3 +177.0 +182.2 +184.4 +175.8 +185.6 +179.6 +180.8 +179.9 +199.4 +175.1 +196.3 +185.5 +182.5 +179.4 +191.2 +192.4 +180.3 +186.8 +194.7 +180.5 +178.0 +180.8 +184.9 +180.7 +179.0 +184.5 +180.5 +176.2 +182.9 +178.8 +183.5 +194.6 +182.0 +198.5 +176.5 +185.0 +187.6 +196.9 +194.2 +185.0 +190.8 +179.6 +175.8 +198.5 +180.0 +177.9 +203.3 +183.3 +181.4 +183.8 +176.5 +175.1 +193.9 +178.9 +183.0 +180.2 +178.0 +178.9 +188.1 +181.3 +179.3 +181.6 +183.1 +175.0 +178.8 +175.7 +179.4 +180.6 +185.9 +176.7 +190.8 +189.6 +180.1 +176.7 +181.7 +180.7 +191.9 +175.5 +180.1 +185.1 +182.0 +181.0 +184.3 +196.6 +188.8 +191.0 +184.2 +184.1 +180.1 +189.0 +178.2 +182.6 +190.4 +182.5 +186.3 +182.9 +191.5 +183.6 +193.2 +183.3 +201.4 +215.5 +182.4 +196.6 +183.6 +190.7 +180.8 +190.9 +178.2 +189.8 +194.8 +180.9 +184.2 +184.1 +185.3 +176.7 +181.5 +181.0 +191.1 +195.9 +192.0 +178.0 +179.9 +184.7 +181.3 +188.5 +187.6 +184.7 +193.9 +198.5 +188.5 +186.5 +184.4 +188.1 +183.0 +190.8 +184.9 +183.8 +184.5 +185.5 +180.2 +181.3 +181.8 +177.5 +178.6 +205.8 +188.5 +188.1 +183.9 +182.9 +192.0 +181.9 +181.5 +190.2 +185.2 +188.1 +174.3 +190.1 +185.6 +183.3 +182.1 +192.2 +179.6 +179.7 +187.5 +187.3 +187.1 +180.0 +184.2 +180.6 +311.7 +185.9 +185.9 +185.1 +176.8 +176.4 +177.7 +177.1 +188.2 +174.8 +184.0 +178.6 +180.2 +183.4 +182.9 +189.8 +187.3 +176.8 +192.5 +186.1 +212.1 +179.4 +181.3 +188.4 +177.7 +200.4 +182.7 +177.5 +183.5 +187.5 +182.6 +194.0 +179.6 +181.6 +201.6 +183.0 +185.5 +177.8 +200.1 +182.7 +182.1 +192.5 +185.6 +188.8 +180.8 +179.5 +181.1 +185.1 +178.1 +178.9 +183.1 +180.8 +178.7 +184.8 +180.1 +179.7 +187.2 +187.1 +182.5 +187.0 +185.6 +191.9 +187.7 +186.3 +188.0 +180.1 +179.3 +181.3 +193.7 +176.8 +184.4 +176.7 +183.1 +189.4 +178.8 +211.4 +182.2 +190.8 +194.1 +179.4 +176.3 +178.6 +175.5 +182.9 +186.0 +184.1 +187.8 +181.3 +188.4 +178.3 +178.7 +200.4 +176.0 +187.7 +197.2 +188.2 +188.6 +192.3 +180.3 +186.3 +187.6 +201.5 +183.8 +182.9 +185.8 +185.9 +181.9 +180.2 +177.3 +183.3 +186.4 +181.3 +195.4 +194.0 +182.7 +178.3 +190.5 +178.6 +194.5 +175.4 +176.8 +177.4 +191.1 +180.6 +175.5 +175.2 +186.9 +176.1 +183.6 +187.1 +178.4 +196.0 +201.0 +183.0 +179.7 +179.9 +184.1 +179.9 +193.1 +180.8 +187.2 +182.0 +176.1 +183.4 +188.4 +177.6 +177.3 +183.8 +179.5 +184.4 +179.8 +181.5 +178.6 +186.5 +183.1 +175.7 +177.3 +177.5 +194.3 +190.9 +175.4 +198.2 +182.6 +180.9 +182.7 +177.0 +190.4 +186.8 +199.4 +183.6 +180.4 +196.3 +205.0 +178.0 +182.0 +185.0 +180.0 +200.6 +185.4 +186.6 +188.4 +199.1 +183.3 +211.8 +182.2 +186.0 +186.5 +185.4 +183.8 +182.3 +179.9 +182.0 +186.9 +184.6 +187.9 +182.4 +187.3 +199.6 +187.9 +196.6 +189.9 +190.9 +180.9 +177.5 +180.2 +188.4 +181.4 +178.0 +190.8 +198.7 +191.5 +189.8 +191.4 +188.6 +186.2 +198.8 +207.4 +192.2 +185.0 +194.7 +198.7 +191.4 +197.4 +184.9 +202.5 +178.6 +180.3 +186.9 +184.5 +178.3 +179.4 +201.3 +193.5 +184.2 +193.9 +177.1 +185.8 +188.5 +183.6 +201.0 +183.6 +190.9 +190.3 +185.4 +180.2 +185.9 +182.6 +184.7 +182.1 +188.7 +180.5 +178.0 +198.6 +188.8 +183.8 +178.1 +176.4 +179.4 +179.5 +186.5 +180.4 +179.8 +180.2 +195.0 +177.2 +180.0 +195.5 +181.7 +183.6 +187.1 +180.7 +180.7 +181.2 +184.5 +181.0 +199.1 +197.4 +183.6 +189.0 +183.2 +196.4 +183.7 +191.4 +185.3 +202.8 +177.9 +182.1 +179.5 +176.3 +193.1 +184.6 +178.8 +191.8 +224.8 +180.5 +182.0 +178.8 +178.2 +178.8 +185.4 +186.5 +178.6 +188.4 +179.7 +190.9 +181.6 +179.8 +190.1 +188.0 +179.7 +204.8 +184.8 +179.7 +182.5 +184.1 +176.1 +181.1 +177.2 +185.8 +177.7 +179.1 +186.7 +183.8 +177.5 +180.2 +200.9 +181.0 +182.7 +177.7 +191.7 +176.8 +190.9 +178.6 +182.3 +178.8 +186.1 +176.8 +180.0 +182.5 +175.0 +183.2 +177.4 +189.5 +177.7 +179.1 +180.8 +177.9 +180.0 +181.6 +178.6 +178.1 +184.7 +176.8 +176.0 +175.7 +175.9 +180.3 +176.3 +180.4 +178.8 +182.5 +177.9 +187.7 +182.2 +187.2 +178.5 +179.9 +181.1 +180.9 +182.5 +179.8 +178.6 +176.0 +179.0 +189.5 +183.5 +182.5 +183.6 +186.0 +181.7 +191.1 +187.4 +194.0 +197.2 +197.3 +177.9 +179.4 +178.0 +176.9 +178.6 +189.0 +183.3 +188.5 +184.0 +184.0 +185.5 +180.7 +176.3 +180.2 +179.9 +186.7 +177.5 +181.0 +189.3 +190.3 +178.3 +188.1 +189.5 +183.6 +190.7 +178.9 +179.1 +179.5 +196.1 +180.8 +178.9 +177.9 +184.6 +178.0 +181.9 +185.3 +187.5 +183.9 +187.0 +180.1 +198.9 +182.6 +181.1 +191.1 +190.7 +183.6 +181.2 +184.5 +177.3 +179.5 +178.7 +181.5 +180.1 +185.0 +178.6 +182.8 +182.7 +192.6 +181.5 +176.5 +178.4 +187.0 +180.2 +187.5 +182.5 +178.0 +184.6 +180.3 +187.2 +177.6 +187.5 +190.6 +188.2 +187.8 +182.6 +186.8 +179.0 +211.0 +176.2 +180.5 +183.4 +215.3 +181.6 +208.2 +215.6 +207.8 +189.1 +176.5 +190.9 +187.0 +178.8 +190.2 +183.0 +193.6 +175.3 +184.4 +184.2 +181.7 +175.4 +177.4 +176.1 +187.1 +194.1 +181.3 +193.1 +182.1 +184.7 +183.1 +186.0 +180.6 +181.7 +181.7 +184.9 +191.4 +201.1 +181.7 +192.9 +177.6 +187.6 +177.6 +183.6 +195.4 +187.2 +184.2 +185.4 +178.0 +201.0 +180.0 +205.9 +201.9 +199.2 +185.2 +182.7 +181.0 +187.4 +181.5 +186.6 +180.4 +176.7 +177.9 +178.2 +179.3 +186.5 +175.7 +185.5 +195.1 +181.9 +183.3 +182.0 +176.7 +181.0 +176.1 +179.4 +186.2 +194.1 +191.1 +184.0 +185.1 +188.6 +183.2 +176.1 +179.3 +183.6 +177.5 +192.7 +180.6 +191.3 +182.6 +189.3 +183.6 +182.4 +178.8 +175.5 +179.3 +193.2 +181.3 +183.9 +185.1 +183.3 +184.9 +188.6 +185.7 +194.9 +199.5 +175.7 +202.6 +194.2 +185.3 +178.0 +182.5 +202.3 +187.5 +192.9 +181.8 +183.2 +183.5 +187.8 +179.6 +178.7 +197.1 +182.2 +202.2 +194.8 +184.9 +192.6 +175.5 +178.0 +184.7 +177.8 +178.7 +179.7 +181.4 +186.6 +179.3 +178.9 +182.3 +180.8 +180.5 +182.4 +184.0 +183.2 +181.2 +183.6 +186.2 +179.7 +178.6 +200.2 +189.2 +177.5 +188.0 +191.1 +183.2 +183.4 +191.0 +186.9 +198.9 +180.0 +175.3 +261.0 +178.7 +181.1 +181.2 +178.7 +179.1 +201.6 +178.4 +181.6 +176.2 +181.7 +192.2 +184.5 +179.1 +191.8 +181.3 +184.4 +185.0 +186.6 +178.6 +180.2 +185.2 +190.0 +177.3 +178.5 +193.7 +178.7 +184.8 +179.9 +192.0 +186.2 +194.7 +181.5 +183.9 +188.0 +183.4 +181.3 +187.2 +184.6 +188.7 +185.3 +180.1 +183.4 +182.4 +176.9 +177.6 +191.1 +180.8 +176.0 +175.4 +180.0 +179.6 +188.3 +191.3 +183.2 +181.7 +177.9 +180.0 +182.0 +181.3 +180.1 +187.7 +177.5 +186.7 +183.4 +189.1 +199.9 +203.9 +184.1 +179.2 +187.4 +185.7 +181.8 +179.7 +179.1 +175.6 +200.8 +181.2 +175.9 +179.3 +176.9 +183.8 +195.7 +185.6 +181.9 +184.0 +190.1 +188.6 +179.0 +181.7 +179.2 +212.9 +181.3 +192.8 +180.8 +180.4 +190.8 +178.1 +181.2 +183.4 +189.4 +191.6 +196.7 +179.2 +203.5 +197.4 +180.5 +184.2 +194.9 +182.6 +183.3 +177.9 +200.4 +191.9 +175.4 +214.3 +184.1 +180.8 +182.9 +181.3 +182.4 +181.4 +191.0 +178.9 +188.3 +195.6 +182.1 +193.5 +180.3 +196.3 +177.9 +198.8 +190.9 +179.1 +185.4 +188.7 +181.0 +177.8 +178.0 +203.4 +179.4 +180.8 +183.6 +179.0 +184.8 +178.0 +188.3 +203.1 +194.9 +196.8 +187.9 +185.7 +178.0 +180.8 +176.8 +181.0 +180.9 +187.0 +179.5 +186.9 +175.9 +174.7 +177.1 +187.9 +193.2 +177.3 +177.9 +181.2 +180.1 +176.1 +179.4 +176.7 +183.2 +191.8 +184.7 +180.8 +183.2 +176.9 +182.8 +187.5 +187.3 +185.7 +184.6 +178.8 +183.0 +176.7 +177.6 +198.0 +194.7 +188.8 +180.9 +187.9 +175.9 +193.1 +183.4 +186.2 +184.4 +182.3 +175.6 +224.3 +186.0 +184.0 +179.0 +176.6 +183.9 +190.9 +188.5 +183.9 +184.7 +198.6 +184.1 +182.0 +181.3 +183.3 +181.6 +182.8 +186.6 +181.4 +179.0 +192.0 +186.3 +202.8 +190.0 +182.1 +186.4 +186.0 +187.0 +192.8 +185.4 +185.4 +181.2 +182.3 +183.2 +190.4 +184.9 +186.4 +180.9 +181.4 +179.8 +191.3 +182.4 +193.0 +202.1 +187.9 +222.0 +196.9 +201.9 +197.8 +198.7 +184.1 +192.7 +203.2 +197.1 +193.7 +196.5 +186.3 +198.4 +181.2 +192.3 +190.4 +183.3 +181.9 +201.8 +198.5 +194.7 +182.3 +184.9 +186.0 +185.9 +190.8 +179.4 +182.2 +181.7 +180.6 +179.2 +180.9 +189.7 +195.7 +200.7 +191.6 +191.1 +187.6 +181.6 +185.9 +188.3 +183.5 +187.7 +190.3 +186.1 +181.1 +178.7 +178.0 +179.0 +191.5 +186.8 +184.9 +181.0 +183.4 +181.3 +180.1 +188.2 +204.3 +180.1 +185.8 +184.9 +187.4 +176.0 +184.2 +177.6 +179.0 +178.9 +188.3 +179.8 +180.9 +177.5 +188.9 +191.1 +184.7 +189.5 +178.4 +189.3 +184.7 +186.2 +178.9 +176.6 +180.8 +177.3 +183.3 +180.0 +188.8 +186.8 +179.5 +179.6 +181.6 +177.4 +176.5 +178.0 +182.6 +184.7 +189.3 +183.6 +180.6 +186.6 +180.7 +185.6 +178.2 +176.3 +181.2 +177.6 +175.7 +177.3 +184.0 +178.2 +182.9 +201.6 +175.0 +179.3 +181.6 +176.2 +180.4 +176.0 +183.7 +177.5 +183.5 +193.5 +192.7 +179.0 +182.2 +179.6 +179.3 +176.8 +177.9 +177.2 +176.5 +177.4 +177.9 +186.5 +178.9 +181.0 +178.0 +176.6 +176.5 +179.2 +187.2 +178.2 +184.6 +178.9 +177.8 +182.1 +187.2 +188.8 +184.5 +181.0 +188.4 +193.1 +178.7 +186.2 +181.1 +183.2 +181.4 +177.6 +176.7 +177.1 +179.0 +181.0 +177.9 +183.5 +181.6 +178.9 +179.4 +175.8 +175.6 +179.0 +180.1 +175.9 +179.9 +178.1 +186.0 +208.0 +178.6 +182.7 +186.3 +174.5 +178.6 +184.3 +185.4 +178.3 +178.5 +190.5 +180.8 +188.2 +177.3 +190.1 +175.5 +188.5 +185.4 +179.6 +181.4 +177.4 +217.5 +196.9 +180.8 +179.9 +192.5 +178.0 +178.4 +175.8 +175.3 +184.8 +197.2 +186.0 +176.9 +176.5 +178.4 +180.8 +185.5 +203.5 +183.3 +180.0 +177.6 +181.2 +180.2 +179.7 +194.4 +180.8 +181.3 +176.9 +187.2 +178.5 +178.7 +185.4 +183.0 +181.3 +261.7 +178.1 +175.9 +175.9 +189.8 +185.9 +184.1 +181.7 +182.7 +177.6 +176.6 +183.6 +178.4 +178.5 +185.8 +184.5 +178.5 +178.0 +181.7 +177.4 +179.3 +177.5 +175.7 +176.6 +176.4 +180.8 +184.9 +177.6 +178.0 +178.7 +178.0 +177.8 +176.7 +183.6 +196.4 +182.4 +182.2 +178.0 +175.9 +179.2 +175.6 +178.3 +176.2 +178.8 +183.1 +175.2 +182.4 +180.6 +177.6 +176.6 +180.2 +180.4 +181.1 +176.2 +188.7 +187.2 +181.6 +181.5 +177.2 +176.9 +177.4 +176.3 +185.2 +178.3 +184.6 +178.3 +189.7 +179.5 +201.1 +176.2 +185.5 +182.2 +183.4 +174.5 +176.8 +180.1 +179.8 +182.2 +178.8 +186.5 +188.2 +180.4 +182.1 +182.8 +194.7 +182.3 +177.6 +176.2 +180.7 +193.6 +178.9 +177.8 +178.7 +179.8 +176.3 +180.8 +184.2 +181.5 +179.2 +178.2 +179.9 +176.7 +180.0 +183.0 +180.6 +177.3 +186.7 +180.8 +181.5 +190.3 +186.8 +184.2 +180.4 +181.6 +182.2 +183.0 +173.3 +187.5 +185.7 +180.1 +177.8 +189.2 +194.9 +176.8 +194.9 +187.0 +209.2 +185.4 +177.7 +178.0 +175.4 +178.0 +183.1 +177.9 +182.4 +177.2 +177.7 +178.9 +183.5 +178.1 +179.2 +191.5 +182.6 +179.8 +182.5 +176.9 +180.2 +176.0 +178.6 +184.7 +279.2 +179.0 +176.5 +180.0 +182.9 +178.3 +188.1 +179.6 +177.8 +175.6 +180.0 +178.6 +181.0 +184.0 +178.2 +191.6 +176.8 +187.7 +179.1 +183.3 +203.5 +178.7 +178.6 +175.4 +187.5 +179.7 +181.6 +177.9 +180.5 +186.7 +182.3 +187.9 +182.2 +187.8 +181.1 +266.6 +186.3 +185.9 +178.0 +180.6 +176.9 +176.5 +177.0 +184.1 +182.7 +179.0 +182.6 +180.8 +179.9 +179.0 +183.1 +178.6 +176.4 +177.3 +176.4 +180.2 +177.2 +177.0 +177.5 +178.3 +175.2 +177.8 +176.7 +181.4 +177.7 +177.1 +176.5 +180.8 +177.2 +178.1 +177.9 +187.1 +183.8 +187.0 +184.4 +180.2 +179.2 +179.2 +178.6 +188.0 +184.7 +178.0 +176.3 +175.3 +181.6 +191.8 +176.9 +182.0 +183.3 +177.5 +185.0 +181.5 +182.6 +179.0 +188.4 +203.6 +184.3 +183.8 +188.0 +189.7 +184.7 +185.9 +195.0 +185.3 +178.8 +190.6 +181.6 +180.1 +187.1 +182.3 +176.8 +179.1 +185.8 +180.1 +180.3 +182.7 +185.3 +175.6 +182.8 +188.5 +175.6 +182.7 +182.0 +179.5 +180.9 +179.4 +183.2 +182.7 +182.5 +182.0 +190.1 +199.7 +177.8 +177.6 +180.9 +181.6 +183.3 +186.1 +188.0 +184.9 +185.4 +182.6 +182.5 +178.7 +185.6 +175.4 +176.7 +182.5 +180.6 +178.3 +179.7 +184.4 +180.9 +184.5 +187.2 +181.6 +192.7 +185.9 +179.6 +184.3 +182.3 +175.0 +180.3 +177.2 +177.9 +178.4 +179.7 +174.6 +190.4 +193.1 +186.8 +180.6 +176.0 +177.4 +188.4 +179.2 +180.9 +178.7 +180.9 +178.2 +176.6 +185.0 +182.1 +181.7 +175.2 +175.1 +183.2 +192.0 +188.3 +182.1 +176.7 +177.6 +177.3 +192.6 +189.3 +178.2 +177.5 +183.1 +183.4 +181.2 +179.5 +175.4 +176.8 +178.2 +175.8 +179.4 +184.7 +184.6 +191.9 +177.4 +176.7 +179.3 +181.6 +183.3 +176.6 +218.6 +175.7 +174.4 +186.4 +185.1 +176.0 +195.2 +180.3 +178.1 +179.2 +178.0 +181.5 +182.9 +179.0 +191.3 +185.1 +186.5 +177.2 +180.3 +191.0 +175.7 +179.8 +182.0 +181.6 +185.0 +183.2 +174.8 +184.5 +182.1 +178.7 +186.7 +186.9 +176.5 +184.5 +188.9 +180.4 +190.1 +181.4 +188.0 +193.5 +187.7 +180.0 +183.5 +176.7 +177.5 +179.2 +206.2 +178.4 +183.2 +181.7 +200.9 +193.1 +176.1 +181.8 +183.6 +193.5 +201.7 +188.0 +178.9 +180.5 +180.8 +176.5 +178.0 +180.6 +181.4 +184.7 +192.4 +177.4 +182.4 +202.8 +189.7 +177.3 +181.5 +178.7 +180.0 +180.6 +178.0 +178.3 +183.9 +177.4 +180.0 +189.7 +175.4 +185.2 +179.3 +184.1 +177.5 +182.1 +174.8 +179.8 +187.5 +216.9 +182.9 +183.5 +203.3 +181.5 +184.0 +181.3 +183.4 +180.3 +176.1 +180.3 +180.1 +180.2 +181.2 +182.3 +176.2 +189.5 +219.7 +180.1 +176.6 +177.3 +178.8 +181.2 +175.1 +183.3 +182.1 +178.7 +180.9 +183.3 +188.1 +225.2 +185.8 +184.3 +187.5 +187.3 +190.7 +181.0 +180.2 +184.6 +177.9 +180.1 +187.6 +181.1 +177.5 +184.5 +176.5 +182.7 +174.5 +183.2 +176.5 +183.0 +177.0 +179.2 +182.9 +184.9 +176.4 +179.9 +183.2 +178.1 +177.0 +205.5 +182.5 +175.2 +185.0 +182.7 +178.2 +189.0 +190.5 +178.8 +183.5 +185.1 +181.3 +181.3 +194.5 +187.8 +184.9 +177.2 +179.6 +195.1 +181.2 +188.8 +180.4 +180.8 +180.0 +177.6 +198.3 +180.6 +179.5 +178.4 +179.3 +177.5 +176.8 +181.6 +183.8 +181.8 +175.6 +181.0 +178.4 +179.4 +179.8 +181.3 +181.1 +193.3 +180.1 +182.8 +179.3 +181.1 +188.3 +177.6 +178.5 +178.8 +177.1 +179.2 +181.6 +185.6 +191.8 +176.3 +187.8 +184.5 +197.3 +183.1 +179.7 +178.1 +181.3 +177.6 +178.2 +179.5 +177.5 +185.4 +184.4 +186.4 +177.4 +178.0 +179.7 +177.9 +184.6 +189.1 +184.5 +197.2 +192.6 +188.0 +186.5 +177.3 +183.7 +183.7 +192.8 +181.2 +199.4 +179.7 +187.7 +192.8 +208.5 +184.1 +175.6 +185.3 +188.6 +183.7 +180.7 +182.1 +176.0 +183.1 +179.6 +175.7 +187.0 +179.9 +184.8 +179.4 +187.6 +181.6 +201.1 +193.5 +186.2 +190.3 +197.9 +208.2 +192.9 +193.1 +184.9 +210.5 +178.6 +198.2 +177.9 +192.2 +181.6 +185.1 +189.1 +188.3 +204.7 +186.2 +179.6 +189.2 +176.9 +180.5 +183.3 +193.1 +178.4 +203.8 +185.2 +180.6 +183.3 +186.7 +188.8 +184.4 +181.7 +186.4 +185.8 +180.1 +182.7 +179.8 +198.9 +180.4 +178.3 +195.6 +184.0 +179.5 +181.8 +187.4 +181.7 +181.8 +175.1 +200.0 +181.6 +203.0 +176.0 +184.9 +199.3 +178.2 +184.3 +176.6 +192.9 +188.1 +185.7 +181.0 +179.5 +183.4 +186.7 +178.6 +183.2 +179.6 +182.6 +177.3 +179.0 +197.0 +182.0 +174.8 +179.4 +180.7 +210.3 +211.3 +235.3 +255.7 +275.7 +286.8 +306.7 +347.2 +381.2 +184.5 +186.7 +190.1 +193.3 +177.9 +188.0 +184.0 +180.6 +178.5 +235.8 +179.6 +178.1 +196.0 +180.5 +179.0 +185.8 +230.8 +211.1 +178.3 +179.4 +186.5 +180.6 +181.4 +183.2 +188.7 +184.8 +175.0 +179.2 +185.4 +180.8 +182.7 +183.1 +177.3 +177.4 +181.3 +190.9 +182.7 +198.2 +180.9 +200.5 +188.4 +207.5 +190.2 +178.9 +184.1 +193.6 +321.3 +183.8 +181.4 +174.6 +185.1 +198.9 +202.2 +183.3 +187.3 +183.0 +184.5 +178.3 +183.8 +176.7 +178.3 +186.0 +181.0 +176.0 +181.3 +202.8 +179.7 +180.3 +191.6 +176.8 +182.6 +183.7 +181.3 +186.2 +205.7 +186.4 +181.9 +189.9 +188.9 +193.6 +203.2 +212.3 +178.9 +181.5 +189.6 +178.9 +177.6 +189.0 +177.3 +177.2 +178.0 +178.5 +177.9 +197.8 +182.0 +185.7 +193.6 +185.8 +183.1 +183.1 +192.7 +183.0 +187.0 +193.4 +186.9 +182.3 +183.9 +201.6 +199.5 +182.7 +184.6 +181.8 +179.1 +189.8 +183.1 +187.8 +185.7 +192.9 +179.3 +179.7 +180.2 +177.6 +180.8 +178.0 +190.2 +177.5 +199.2 +192.7 +181.2 +185.2 +183.5 +198.1 +178.9 +183.7 +191.6 +187.4 +180.5 +211.9 +206.2 +183.2 +182.1 +176.7 +187.4 +176.6 +183.2 +195.5 +178.7 +180.7 +181.2 +189.1 +187.8 +183.1 +181.9 +186.3 +197.1 +184.7 +177.2 +180.5 +179.5 +181.6 +177.9 +187.7 +196.2 +189.5 +180.5 +183.3 +180.6 +186.7 +186.2 +177.7 +192.1 +182.4 +177.5 +179.7 +193.6 +200.1 +179.9 +188.4 +182.3 +190.4 +179.4 +182.4 +181.7 +195.7 +177.2 +181.3 +182.9 +188.3 +183.6 +180.3 +192.7 +183.1 +210.5 +185.1 +183.4 +191.6 +187.9 +183.1 +175.5 +177.4 +183.7 +182.9 +188.5 +180.7 +186.2 +182.4 +186.3 +182.4 +188.1 +180.2 +195.4 +181.8 +192.9 +176.3 +177.7 +178.8 +179.4 +182.6 +180.3 +187.4 +185.3 +183.5 +180.5 +180.8 +182.0 +178.9 +176.2 +197.0 +181.5 +177.1 +189.1 +188.0 +176.5 +181.7 +184.9 +192.2 +181.5 +189.0 +204.6 +177.5 +176.6 +202.2 +176.9 +181.9 +177.9 +180.6 +197.2 +175.9 +175.6 +183.4 +182.7 +191.2 +178.8 +187.2 +178.9 +192.4 +178.6 +178.4 +184.5 +216.4 +190.7 +182.7 +181.6 +199.5 +182.0 +179.1 +189.1 +180.3 +182.0 +185.6 +196.1 +182.5 +180.4 +190.9 +178.9 +186.3 +209.4 +181.7 +186.6 +192.0 +200.0 +205.7 +191.0 +186.0 +185.5 +176.2 +191.4 +184.4 +186.1 +186.6 +180.0 +180.8 +178.0 +180.0 +198.8 +183.9 +183.5 +176.4 +176.7 +181.4 +186.4 +186.8 +186.0 +179.5 +187.2 +179.5 +185.2 +179.4 +183.6 +188.6 +179.8 +179.0 +184.4 +178.4 +177.4 +195.2 +207.0 +184.3 +179.6 +183.1 +184.0 +187.2 +181.4 +178.4 +182.7 +189.4 +183.1 +179.7 +181.8 +181.5 +181.9 +182.6 +178.2 +189.2 +189.1 +190.3 +184.1 +195.3 +176.1 +177.3 +186.8 +183.7 +188.5 +179.7 +177.4 +181.4 +180.7 +206.9 +179.2 +177.8 +180.5 +180.6 +191.9 +181.7 +190.3 +183.5 +186.2 +175.8 +179.1 +181.6 +181.6 +183.0 +179.1 +179.3 +183.7 +181.2 +185.2 +176.8 +177.8 +175.3 +184.6 +179.2 +176.9 +188.0 +184.6 +184.6 +180.9 +189.9 +396.3 +176.1 +183.1 +179.2 +184.6 +181.5 +181.3 +176.2 +177.4 +174.3 +179.4 +178.8 +184.6 +182.1 +183.4 +178.4 +184.1 +182.7 +181.4 +177.7 +185.0 +177.2 +179.4 +183.5 +183.1 +187.6 +181.1 +179.7 +189.6 +183.6 +177.1 +181.5 +193.1 +196.8 +180.2 +177.3 +178.8 +176.6 +177.8 +190.0 +176.3 +180.1 +177.5 +187.2 +186.7 +176.5 +179.6 +177.2 +177.5 +196.4 +181.9 +179.9 +178.2 +181.5 +177.5 +179.9 +183.1 +181.5 +181.8 +177.4 +177.6 +174.5 +178.4 +183.8 +181.6 +186.4 +178.1 +177.3 +179.1 +183.6 +191.2 +181.7 +187.6 +186.1 +183.5 +185.0 +186.5 +187.5 +190.5 +224.7 +177.3 +189.2 +191.7 +188.0 +182.8 +187.8 +182.2 +241.9 +197.6 +188.3 +181.7 +195.5 +184.4 +190.9 +187.4 +195.0 +190.0 +180.5 +179.1 +180.3 +195.0 +176.7 +191.8 +200.2 +180.3 +180.6 +186.1 +178.2 +182.5 +183.2 +178.9 +177.6 +185.6 +203.5 +179.7 +179.6 +190.8 +187.4 +184.7 +185.1 +195.1 +191.9 +181.1 +183.1 +177.1 +177.9 +178.5 +178.8 +186.0 +189.9 +184.7 +190.4 +192.9 +178.6 +176.1 +189.2 +186.1 +178.6 +186.9 +181.5 +181.5 +181.2 +178.1 +181.8 +187.6 +183.3 +188.5 +179.6 +183.5 +187.9 +179.6 +192.4 +182.6 +192.1 +255.4 +266.2 +191.5 +194.8 +179.7 +190.8 +182.8 +182.3 +186.7 +186.4 +178.1 +182.5 +175.0 +187.7 +199.4 +182.1 +180.5 +182.9 +175.9 +219.6 +207.0 +196.5 +190.7 +199.1 +185.4 +174.5 +180.5 +182.7 +181.8 +189.2 +181.0 +198.2 +191.9 +185.3 +182.9 +188.5 +178.7 +179.2 +188.7 +188.5 +178.2 +197.6 +178.3 +192.7 +181.9 +191.2 +181.7 +176.1 +196.5 +187.7 +190.5 +187.8 +182.9 +189.9 +192.3 +177.4 +184.7 +178.3 +185.8 +182.0 +180.1 +183.1 +182.0 +176.1 +185.2 +196.0 +185.5 +179.1 +198.1 +197.5 +182.0 +177.0 +178.3 +202.9 +177.0 +180.6 +181.6 +191.6 +179.6 +180.9 +183.7 +188.4 +182.6 +185.7 +191.5 +178.6 +188.4 +184.0 +183.8 +189.3 +185.2 +192.9 +195.4 +197.1 +190.4 +186.5 +196.0 +189.8 +191.9 +187.3 +185.7 +198.0 +175.8 +183.7 +176.0 +179.9 +183.1 +186.7 +177.9 +179.2 +181.7 +184.9 +181.6 +178.5 +176.5 +176.7 +178.4 +184.7 +181.3 +182.3 +182.8 +182.6 +190.2 +177.0 +187.9 +188.1 +186.4 +187.8 +181.3 +187.7 +191.8 +182.5 +182.0 +180.1 +185.8 +177.8 +192.5 +185.2 +179.7 +179.3 +191.0 +181.3 +192.9 +180.3 +194.5 +187.6 +184.3 +194.7 +182.7 +177.7 +181.2 +177.2 +184.0 +181.8 +188.5 +187.8 +184.7 +181.7 +184.6 +177.4 +193.0 +178.7 +176.5 +176.9 +180.9 +180.8 +187.8 +179.8 +175.2 +177.5 +180.5 +186.6 +177.0 +177.8 +177.8 +182.5 +180.0 +182.4 +188.6 +192.5 +177.5 +179.8 +178.8 +178.9 +182.0 +180.6 +184.5 +179.9 +175.4 +183.1 +177.8 +177.2 +191.7 +184.6 +183.1 +175.6 +200.6 +221.5 +177.2 +177.6 +184.0 +183.4 +199.2 +184.6 +186.1 +180.3 +188.1 +184.1 +185.9 +178.0 +183.7 +186.7 +186.1 +177.8 +183.5 +178.0 +177.0 +185.6 +187.0 +178.9 +178.9 +200.5 +180.2 +181.6 +187.8 +179.8 +182.0 +184.3 +176.3 +182.8 +184.3 +181.9 +185.3 +189.2 +177.3 +183.6 +186.7 +184.0 +243.9 +175.9 +184.3 +176.0 +179.6 +178.5 +182.3 +189.7 +179.6 +190.3 +188.8 +175.2 +179.8 +181.0 +175.6 +175.3 +187.5 +175.3 +186.8 +176.6 +177.3 +175.7 +178.5 +178.8 +178.5 +178.9 +183.7 +182.2 +182.3 +178.9 +182.0 +178.6 +184.2 +178.8 +176.7 +176.9 +176.2 +181.1 +177.9 +177.2 +178.6 +194.8 +185.4 +188.3 +178.9 +187.1 +184.3 +184.2 +190.3 +193.8 +185.0 +181.0 +181.4 +181.0 +191.8 +177.4 +178.6 +179.6 +180.2 +179.0 +187.4 +177.1 +182.7 +181.9 +177.9 +184.8 +195.1 +181.2 +182.1 +187.4 +186.8 +184.7 +181.9 +188.7 +191.8 +184.7 +185.5 +183.5 +180.8 +206.1 +179.1 +194.3 +178.0 +177.4 +181.0 +181.7 +190.6 +189.3 +176.3 +183.5 +178.9 +179.4 +187.3 +182.5 +181.7 +183.3 +188.9 +181.3 +195.6 +180.4 +194.0 +182.6 +176.6 +190.8 +176.3 +186.1 +185.1 +181.5 +179.2 +178.3 +179.6 +181.5 +181.0 +183.2 +182.6 +202.6 +183.7 +188.2 +200.6 +177.9 +184.2 +192.0 +180.9 +177.3 +177.2 +188.1 +184.2 +199.4 +185.3 +189.7 +199.8 +176.6 +189.2 +186.3 +182.8 +180.0 +190.8 +188.5 +194.6 +184.8 +202.7 +189.8 +183.2 +206.2 +175.3 +202.1 +193.5 +210.1 +200.5 +194.8 +190.0 +189.3 +182.5 +183.3 +187.1 +186.1 +208.1 +191.4 +207.5 +188.6 +176.7 +190.8 +189.1 +177.2 +189.3 +177.3 +196.2 +195.3 +188.0 +194.1 +181.3 +177.8 +186.2 +184.4 +182.0 +182.5 +187.1 +179.8 +177.4 +184.0 +180.5 +182.9 +176.4 +175.9 +176.8 +182.8 +208.0 +196.3 +183.4 +178.5 +189.7 +183.3 +182.5 +182.7 +182.5 +195.7 +183.8 +182.6 +190.0 +191.0 +191.7 +177.2 +184.1 +202.9 +189.8 +185.0 +183.3 +194.2 +182.8 +178.9 +184.9 +189.1 +192.8 +181.1 +190.5 +205.2 +179.1 +191.3 +186.8 +188.9 +178.8 +188.0 +184.1 +183.1 +182.1 +181.9 +191.6 +183.4 +189.9 +180.2 +186.6 +180.3 +182.7 +184.8 +178.4 +180.3 +193.7 +181.5 +186.6 +179.1 +184.3 +192.1 +189.5 +182.2 +296.4 +194.8 +179.0 +183.4 +194.3 +199.5 +182.7 +181.0 +181.8 +186.9 +186.4 +188.3 +179.2 +190.1 +184.9 +187.8 +186.3 +183.4 +183.1 +191.3 +188.5 +188.5 +187.9 +188.7 +175.3 +178.4 +197.5 +185.3 +201.3 +190.9 +210.1 +179.0 +176.3 +179.4 +195.0 +179.0 +176.4 +188.5 +180.5 +179.5 +179.4 +177.5 +185.2 +179.9 +178.3 +183.8 +193.9 +183.1 +187.7 +192.7 +181.3 +184.1 +182.5 +178.2 +184.0 +195.4 +179.7 +191.7 +183.7 +185.6 +188.4 +195.0 +184.8 +181.8 +186.2 +177.4 +189.8 +192.1 +192.9 +187.5 +191.5 +189.1 +195.7 +188.0 +192.7 +190.1 +190.1 +196.3 +192.4 +193.1 +178.9 +181.9 +179.2 +188.4 +179.2 +187.5 +179.7 +185.3 +177.2 +180.5 +182.2 +203.8 +186.1 +181.7 +180.8 +187.9 +177.4 +189.5 +189.3 +176.5 +179.2 +190.5 +180.0 +177.1 +183.7 +188.8 +180.6 +220.6 +190.9 +197.9 +182.8 +183.0 +177.9 +182.2 +182.4 +201.5 +181.1 +177.8 +189.6 +193.4 +180.3 +180.2 +175.3 +177.8 +178.3 +178.8 +190.7 +181.8 +185.1 +190.2 +179.1 +179.9 +179.8 +180.6 +181.6 +180.8 +184.6 +184.9 +181.7 +176.3 +182.2 +186.9 +227.1 +184.9 +184.5 +177.5 +180.2 +187.5 +186.1 +183.8 +177.4 +184.1 +188.1 +180.4 +197.4 +209.6 +198.3 +179.5 +183.9 +183.5 +181.8 +178.9 +195.5 +184.7 +191.3 +193.5 +196.8 +197.0 +184.2 +181.9 +187.5 +188.7 +177.7 +186.7 +191.6 +192.8 +194.8 +193.6 +194.7 +187.6 +197.2 +187.5 +176.0 +194.4 +199.9 +188.2 +184.5 +183.2 +186.0 +183.6 +210.5 +183.7 +190.4 +188.6 +179.8 +183.9 +210.2 +194.0 +190.8 +188.3 +186.2 +178.0 +178.3 +194.4 +184.5 +182.3 +179.1 +183.4 +182.9 +185.1 +180.6 +182.1 +189.4 +181.6 +180.5 +190.4 +182.1 +204.0 +192.7 +180.4 +190.7 +179.5 +179.2 +181.5 +186.1 +195.1 +189.7 +189.9 +185.9 +206.2 +187.5 +178.5 +206.2 +208.5 +187.4 +182.0 +192.9 +193.0 +196.5 +190.1 +179.7 +205.9 +187.7 +211.5 +177.5 +185.1 +205.3 +178.9 +179.6 +178.8 +185.9 +180.1 +179.0 +178.2 +181.9 +182.9 +191.5 +194.8 +184.9 +177.9 +186.0 +178.7 +175.2 +178.1 +183.9 +176.2 +196.3 +195.0 +214.4 +177.2 +175.2 +181.4 +191.4 +189.0 +179.6 +181.8 +177.8 +175.9 +176.1 +183.8 +182.1 +178.4 +182.1 +180.6 +177.4 +178.9 +178.7 +182.0 +176.5 +182.1 +189.2 +188.3 +184.6 +177.6 +184.6 +183.6 +182.6 +181.5 +198.4 +179.6 +179.3 +185.1 +189.8 +183.5 +180.9 +175.7 +182.6 +179.1 +181.3 +181.4 +189.8 +183.7 +185.8 +185.9 +178.1 +182.3 +196.3 +220.0 +199.4 +191.3 +186.7 +181.7 +180.7 +180.9 +176.2 +185.3 +180.3 +182.5 +177.5 +182.4 +184.8 +191.3 +185.5 +184.4 +193.8 +186.5 +178.9 +180.6 +182.7 +180.1 +184.8 +179.7 +187.8 +183.7 +190.9 +192.5 +205.5 +193.1 +180.7 +186.4 +179.3 +194.2 +187.8 +190.6 +178.8 +176.7 +181.2 +187.6 +191.3 +182.4 +204.9 +191.0 +193.8 +194.0 +198.4 +181.7 +186.5 +186.4 +182.5 +182.2 +179.0 +180.6 +180.0 +181.1 +178.6 +178.6 +183.3 +189.1 +179.9 +190.2 +187.3 +185.3 +185.0 +185.9 +189.3 +182.6 +183.5 +190.8 +183.4 +185.2 +197.1 +187.5 +182.2 +180.4 +181.3 +178.8 +182.5 +183.2 +174.8 +180.4 +200.9 +181.5 +185.5 +176.6 +179.0 +187.4 +173.8 +192.6 +179.3 +188.7 +198.9 +177.8 +185.5 +177.1 +213.1 +185.7 +189.7 +186.6 +189.2 +188.1 +184.0 +184.5 +178.7 +183.9 +186.3 +199.0 +188.2 +191.9 +181.6 +177.4 +183.8 +179.2 +182.7 +182.1 +178.2 +187.3 +189.0 +188.1 +190.8 +184.1 +188.0 +178.9 +195.8 +179.3 +204.6 +183.3 +178.8 +178.8 +176.9 +179.9 +186.0 +181.6 +199.5 +179.6 +189.0 +204.2 +189.7 +181.2 +184.5 +193.4 +183.7 +180.2 +186.2 +191.0 +195.1 +179.9 +182.6 +188.7 +184.5 +178.9 +194.1 +191.5 +179.9 +198.8 +183.4 +212.7 +179.0 +181.8 +176.5 +181.9 +179.7 +186.2 +187.0 +181.6 +184.9 +199.7 +185.4 +186.1 +179.3 +200.6 +187.7 +195.2 +185.5 +182.2 +189.4 +184.6 +177.9 +182.5 +182.9 +191.4 +192.5 +186.2 +178.4 +197.8 +175.7 +183.2 +187.5 +192.9 +175.8 +195.1 +185.7 +188.6 +191.6 +177.0 +181.8 +188.2 +202.4 +183.8 +184.5 +198.6 +186.4 +180.7 +188.6 +181.4 +178.9 +186.5 +185.5 +187.7 +187.7 +179.1 +184.2 +185.1 +178.0 +182.0 +197.0 +194.9 +193.6 +177.0 +186.6 +179.8 +186.1 +177.7 +182.1 +181.3 +178.0 +184.8 +208.3 +193.1 +187.2 +207.7 +188.2 +186.1 +178.1 +193.3 +182.1 +199.8 +182.7 +196.4 +197.9 +178.8 +191.4 +180.9 +180.5 +184.6 +177.2 +182.4 +183.7 +178.2 +187.1 +178.5 +181.3 +190.5 +179.0 +181.0 +178.0 +185.5 +179.1 +184.9 +184.7 +177.7 +176.7 +183.5 +196.3 +184.2 +190.0 +189.4 +186.0 +181.7 +186.0 +188.7 +187.5 +184.6 +176.3 +177.4 +185.9 +183.4 +179.4 +175.8 +186.1 +178.8 +179.0 +181.6 +175.2 +177.4 +176.8 +177.9 +197.6 +184.9 +176.0 +177.6 +178.4 +184.6 +178.1 +183.2 +180.2 +191.3 +180.0 +176.2 +180.3 +181.8 +187.4 +181.7 +177.1 +182.9 +190.6 +183.9 +177.9 +187.4 +174.9 +186.4 +192.0 +190.2 +198.4 +183.6 +206.3 +184.2 +181.0 +195.6 +277.2 +189.9 +187.0 +196.6 +180.6 +183.0 +208.8 +193.1 +188.9 +186.4 +179.9 +181.7 +185.2 +189.5 +188.0 +181.2 +182.5 +183.5 +190.3 +179.3 +182.3 +179.5 +184.2 +193.3 +178.5 +178.5 +182.3 +184.7 +181.9 +185.0 +181.6 +180.0 +202.6 +178.9 +176.4 +185.0 +190.3 +179.4 +182.9 +194.1 +185.2 +198.6 +177.6 +181.1 +179.8 +183.1 +187.7 +187.5 +185.9 +191.2 +183.1 +184.6 +193.0 +197.6 +183.3 +184.4 +181.6 +184.9 +178.0 +177.4 +181.0 +178.9 +180.4 +181.4 +188.0 +178.2 +181.2 +179.0 +188.1 +176.0 +176.4 +179.6 +179.1 +182.2 +185.6 +176.2 +180.0 +195.4 +177.4 +185.2 +177.5 +181.3 +187.8 +176.7 +200.8 +179.8 +183.8 +179.9 +190.4 +180.3 +178.5 +182.7 +180.3 +179.1 +178.8 +184.3 +176.7 +181.8 +179.9 +175.7 +181.6 +184.5 +183.3 +214.3 +175.7 +188.8 +180.1 +179.9 +182.8 +175.0 +177.4 +217.5 +183.7 +178.3 +184.0 +182.2 +175.4 +184.0 +181.2 +184.5 +174.6 +176.0 +180.1 +173.6 +190.4 +177.5 +181.3 +176.1 +181.5 +199.2 +176.2 +186.0 +178.6 +186.9 +187.1 +172.8 +175.5 +182.1 +175.9 +200.4 +193.1 +178.0 +177.1 +180.6 +182.2 +179.0 +175.8 +182.0 +188.0 +180.1 +207.1 +187.0 +180.6 +183.8 +192.9 +175.7 +193.6 +178.3 +182.2 +179.9 +177.2 +181.3 +177.0 +201.1 +176.2 +175.5 +183.1 +181.3 +176.0 +187.7 +185.7 +179.2 +183.4 +184.8 +176.3 +179.3 +175.2 +185.4 +181.2 +177.2 +179.9 +175.1 +175.7 +193.8 +179.7 +184.6 +181.2 +187.8 +184.9 +182.3 +184.9 +181.7 +186.0 +187.0 +178.5 +174.9 +180.7 +188.9 +181.4 +181.3 +176.2 +182.0 +183.4 +181.3 +181.3 +175.0 +179.7 +186.6 +183.7 +185.1 +180.8 +179.6 +180.6 +193.6 +187.6 +181.0 +176.3 +191.0 +177.5 +186.9 +173.8 +184.1 +178.1 +183.2 +180.2 +194.2 +175.8 +183.6 +175.4 +176.6 +183.7 +177.9 +176.4 +180.8 +179.9 +177.9 +183.0 +176.9 +181.3 +182.2 +182.3 +183.8 +181.1 +180.6 +189.9 +185.6 +184.5 +185.3 +175.3 +192.4 +195.5 +188.2 +180.3 +196.3 +184.1 +178.7 +179.7 +187.0 +177.1 +180.7 +177.5 +177.1 +180.8 +182.0 +177.0 +177.0 +193.1 +194.6 +190.0 +183.5 +180.5 +178.4 +177.4 +178.7 +184.6 +189.6 +188.1 +186.1 +184.2 +178.9 +176.7 +195.8 +178.1 +187.6 +175.4 +180.3 +185.3 +189.0 +177.2 +188.9 +179.0 +184.0 +179.1 +187.3 +199.7 +178.2 +179.1 +175.8 +179.8 +178.8 +196.3 +185.0 +196.3 +181.1 +185.5 +175.8 +189.2 +179.4 +192.0 +182.6 +177.9 +181.1 +179.6 +184.2 +194.6 +185.9 +181.4 +176.5 +178.3 +176.3 +179.1 +175.3 +180.5 +186.7 +177.5 +192.3 +187.7 +178.4 +189.4 +180.4 +180.5 +189.9 +183.8 +190.3 +195.5 +185.6 +177.2 +177.7 +178.7 +189.9 +183.8 +193.1 +185.2 +180.5 +180.5 +191.6 +175.0 +176.6 +178.6 +188.8 +180.7 +185.5 +181.2 +199.5 +193.2 +193.8 +185.8 +182.0 +178.5 +188.7 +190.1 +178.7 +181.5 +180.9 +186.6 +189.8 +194.4 +180.1 +193.0 +178.2 +183.2 +182.6 +177.8 +212.2 +177.5 +178.5 +181.9 +185.8 +177.2 +182.9 +176.7 +175.1 +180.4 +186.0 +179.1 +179.5 +177.2 +182.1 +177.8 +180.1 +180.1 +177.9 +183.6 +189.2 +193.2 +179.6 +176.9 +181.3 +178.4 +177.4 +189.2 +182.1 +195.2 +193.0 +184.5 +180.4 +184.1 +176.2 +180.1 +179.0 +174.8 +181.1 +190.0 +179.9 +192.2 +204.9 +179.2 +180.5 +174.7 +181.6 +181.5 +185.1 +176.7 +184.9 +191.9 +182.2 +182.7 +210.5 +186.1 +181.1 +179.3 +186.4 +176.0 +177.4 +180.9 +187.0 +192.2 +182.9 +182.2 +184.0 +180.3 +177.4 +178.7 +176.8 +176.4 +176.1 +182.0 +178.8 +175.2 +187.6 +184.9 +184.3 +188.7 +178.3 +190.9 +186.3 +175.2 +185.8 +175.6 +184.8 +181.1 +176.8 +179.0 +179.2 +196.5 +175.0 +182.6 +194.4 +173.7 +180.0 +178.3 +181.1 +179.5 +177.6 +185.1 +183.2 +188.6 +186.8 +182.5 +176.4 +175.1 +176.9 +182.4 +180.1 +181.7 +182.8 +184.1 +175.7 +178.7 +177.1 +178.3 +177.3 +176.7 +179.8 +176.8 +177.2 +200.5 +175.7 +181.1 +174.5 +179.3 +183.1 +178.4 +189.2 +186.0 +177.4 +178.3 +187.1 +181.0 +183.3 +191.4 +182.9 +182.6 +199.2 +181.4 +180.5 +180.4 +176.7 +184.4 +189.0 +188.1 +197.0 +181.2 +176.1 +181.0 +180.5 +176.5 +214.9 +178.3 +178.7 +181.4 +180.9 +178.0 +185.8 +182.5 +181.8 +177.4 +186.7 +183.2 +183.2 +183.1 +173.9 +181.1 +179.1 +180.3 +177.7 +181.5 +176.5 +177.1 +189.6 +191.5 +183.2 +180.0 +180.9 +183.3 +188.3 +178.4 +185.4 +176.1 +183.2 +181.1 +180.3 +176.4 +177.5 +186.8 +176.3 +338.9 +184.9 +177.0 +187.1 +175.8 +180.0 +182.5 +184.9 +175.2 +179.6 +176.9 +178.7 +177.5 +178.3 +178.9 +180.7 +182.6 +179.3 +177.0 +180.5 +179.2 +177.2 +188.0 +194.8 +183.6 +177.3 +179.1 +185.3 +177.5 +187.6 +180.9 +188.0 +174.8 +183.6 +183.0 +182.9 +178.1 +182.0 +185.9 +176.5 +182.1 +188.5 +179.9 +176.0 +184.3 +185.9 +179.2 +184.3 +181.3 +186.3 +186.7 +177.5 +188.9 +178.8 +211.3 +188.0 +178.5 +192.6 +176.4 +187.1 +189.5 +195.7 +188.5 +185.1 +182.5 +176.6 +182.5 +187.3 +180.5 +191.9 +178.3 +177.1 +188.3 +181.0 +178.0 +187.1 +175.1 +182.3 +177.4 +177.6 +181.2 +179.2 +183.8 +183.2 +173.3 +177.6 +178.6 +179.4 +180.5 +183.9 +176.3 +178.4 +178.9 +175.9 +179.2 +175.4 +183.5 +188.9 +176.5 +185.8 +179.7 +196.8 +185.9 +178.4 +176.4 +177.6 +176.0 +177.9 +181.7 +176.2 +180.6 +179.9 +186.0 +177.2 +178.9 +179.1 +186.6 +176.0 +194.6 +179.2 +181.5 +176.2 +177.2 +179.3 +181.3 +179.2 +181.7 +177.5 +186.6 +179.5 +177.4 +179.4 +177.9 +181.9 +181.3 +177.4 +176.0 +177.8 +185.2 +180.8 +181.3 +182.1 +178.2 +179.5 +185.6 +179.5 +182.5 +186.1 +188.7 +178.7 +186.8 +179.2 +197.7 +180.8 +181.4 +178.1 +207.1 +182.7 +185.5 +186.1 +180.1 +176.1 +177.6 +174.9 +176.1 +180.6 +184.5 +179.2 +186.1 +180.5 +179.5 +179.3 +182.2 +181.8 +184.6 +180.0 +177.1 +176.7 +177.6 +180.8 +176.5 +177.3 +176.8 +176.4 +188.7 +187.6 +178.5 +178.0 +179.3 +181.4 +183.2 +193.8 +178.3 +181.2 +180.4 +183.5 +179.8 +177.4 +183.2 +183.3 +176.6 +176.2 +216.9 +177.8 +177.0 +178.0 +175.3 +189.4 +182.6 +182.9 +183.6 +180.4 +177.2 +176.0 +177.9 +175.1 +182.1 +178.3 +178.5 +186.2 +177.7 +178.0 +183.3 +180.4 +197.6 +181.7 +179.9 +184.0 +194.8 +184.8 +189.4 +191.8 +176.6 +186.2 +180.3 +177.7 +197.0 +187.0 +188.1 +190.1 +176.8 +179.3 +185.4 +174.7 +189.6 +184.2 +178.0 +180.4 +183.4 +179.5 +177.7 +178.7 +186.9 +183.7 +177.2 +173.6 +184.7 +180.4 +183.1 +175.6 +179.6 +184.8 +185.6 +176.2 +177.7 +180.0 +174.7 +183.1 +181.6 +178.0 +178.8 +185.4 +181.2 +178.0 +188.7 +177.1 +189.3 +177.6 +180.6 +181.6 +175.9 +180.7 +179.1 +182.4 +186.7 +175.8 +185.1 +176.7 +180.3 +195.7 +191.3 +191.1 +181.3 +180.5 +179.3 +184.3 +177.1 +182.4 +179.4 +188.8 +174.5 +177.2 +178.4 +179.8 +183.3 +175.4 +184.1 +176.8 +182.4 +177.8 +178.7 +178.6 +189.6 +182.0 +191.2 +179.8 +179.3 +178.6 +183.0 +188.8 +205.3 +180.1 +190.6 +184.4 +179.9 +178.6 +174.7 +180.6 +193.3 +175.3 +175.5 +193.9 +180.4 +177.2 +175.6 +177.0 +182.3 +183.5 +181.9 +179.6 +182.8 +180.9 +180.6 +183.7 +185.0 +176.7 +190.5 +176.2 +179.0 +178.8 +180.0 +181.5 +174.3 +177.3 +179.2 +178.3 +184.7 +174.2 +177.3 +178.0 +178.9 +182.9 +189.8 +183.3 +183.0 +182.5 +176.3 +176.8 +177.5 +176.2 +183.1 +175.4 +175.0 +182.5 +174.9 +185.8 +181.4 +178.4 +183.8 +178.0 +265.0 +178.5 +185.0 +177.4 +179.2 +175.8 +183.0 +184.5 +195.3 +178.5 +178.2 +179.7 +175.0 +187.5 +182.7 +180.3 +181.0 +177.5 +181.5 +179.9 +179.0 +180.0 +182.6 +178.5 +179.6 +181.4 +188.0 +188.2 +179.1 +180.8 +178.5 +181.3 +179.9 +182.0 +181.8 +181.3 +186.6 +197.8 +205.8 +188.2 +194.9 +182.2 +184.6 +188.9 +189.4 +183.4 +179.2 +182.6 +184.6 +183.6 +184.3 +182.5 +181.1 +181.6 +178.9 +179.7 +176.3 +179.4 +180.2 +178.4 +179.6 +190.1 +178.5 +174.4 +177.3 +176.1 +175.3 +175.0 +178.9 +175.1 +181.3 +175.0 +176.0 +192.2 +189.2 +182.4 +179.2 +173.4 +177.9 +178.4 +175.5 +175.9 +199.0 +196.6 +181.7 +181.2 +177.6 +174.7 +174.3 +181.3 +178.9 +203.2 +176.3 +175.8 +192.3 +180.8 +184.8 +179.9 +182.4 +180.0 +174.3 +187.2 +181.4 +176.0 +174.4 +182.4 +186.8 +191.1 +177.1 +178.2 +176.8 +182.6 +184.6 +181.5 +185.4 +178.9 +176.6 +179.6 +175.1 +179.9 +176.9 +176.7 +186.2 +192.9 +183.5 +182.4 +175.5 +176.9 +180.6 +185.7 +174.2 +174.2 +178.9 +178.8 +179.2 +194.9 +177.5 +174.0 +179.0 +180.7 +174.0 +180.0 +178.3 +179.8 +180.0 +175.6 +174.9 +180.9 +177.3 +212.6 +196.0 +183.1 +186.5 +178.4 +185.9 +185.5 +176.8 +178.7 +174.0 +176.6 +179.0 +182.3 +176.6 +185.0 +175.7 +178.5 +176.4 +176.3 +186.3 +222.5 +185.7 +177.1 +183.6 +176.9 +184.0 +175.7 +174.4 +184.2 +269.8 +182.5 +174.6 +178.5 +176.1 +178.9 +177.9 +181.9 +176.8 +174.2 +181.6 +191.2 +179.1 +175.3 +182.2 +180.1 +175.2 +182.3 +175.6 +177.1 +175.1 +174.7 +188.2 +176.9 +183.6 +175.9 +175.5 +174.7 +182.4 +181.2 +179.5 +190.3 +184.8 +237.1 +178.3 +178.7 +184.1 +174.5 +178.3 +178.6 +181.8 +182.2 +190.2 +179.9 +181.0 +180.3 +177.2 +179.2 +179.2 +178.2 +183.0 +177.7 +180.1 +182.6 +196.6 +178.9 +177.0 +176.1 +178.3 +182.9 +176.5 +209.4 +181.1 +199.5 +186.3 +177.6 +182.7 +185.4 +180.2 +186.2 +187.4 +176.4 +182.8 +221.0 +179.0 +178.9 +178.4 +186.8 +174.1 +174.9 +176.8 +190.0 +178.9 +182.4 +178.7 +177.6 +179.4 +173.6 +178.1 +179.8 +177.8 +177.5 +187.5 +209.5 +178.0 +177.2 +179.3 +179.8 +179.8 +174.4 +177.1 +180.2 +182.5 +181.1 +184.7 +179.6 +193.0 +182.7 +181.6 +177.7 +178.2 +181.6 +186.6 +177.2 +178.1 +189.3 +174.3 +180.2 +186.2 +189.8 +185.9 +176.8 +178.8 +175.1 +175.5 +179.1 +180.9 +186.1 +187.0 +186.2 +187.6 +187.5 +197.8 +180.4 +196.7 +176.5 +177.6 +176.2 +180.8 +175.9 +182.8 +178.8 +184.6 +179.5 +175.6 +187.5 +175.7 +201.6 +188.2 +183.4 +184.2 +178.7 +177.0 +174.9 +181.9 +177.6 +177.7 +179.6 +176.0 +178.3 +181.5 +177.5 +184.7 +183.9 +180.9 +178.9 +177.8 +176.6 +188.4 +191.0 +189.6 +178.1 +177.9 +186.1 +178.7 +184.3 +176.5 +179.6 +174.1 +184.4 +176.8 +182.3 +179.5 +188.8 +180.2 +176.1 +198.4 +178.4 +188.6 +191.0 +180.4 +179.5 +176.0 +188.1 +189.6 +192.6 +177.0 +189.1 +177.5 +178.8 +186.8 +178.6 +180.5 +184.0 +178.7 +179.0 +176.2 +176.7 +185.2 +192.3 +178.0 +175.7 +181.0 +183.0 +175.7 +174.6 +176.7 +177.8 +179.0 +177.4 +176.7 +182.5 +194.0 +181.4 +178.7 +176.2 +176.7 +204.2 +178.3 +190.6 +188.5 +177.5 +180.6 +178.1 +175.2 +276.8 +353.6 +297.8 +235.1 +211.7 +213.4 +213.5 +191.5 +178.8 +186.2 +175.4 +178.2 +184.5 +179.9 +182.0 +179.3 +178.6 +192.5 +177.7 +179.2 +194.9 +183.1 +178.6 +179.9 +192.5 +184.1 +186.6 +181.2 +175.4 +197.2 +179.6 +184.6 +197.6 +180.6 +189.6 +175.3 +185.6 +174.5 +178.9 +174.2 +179.6 +173.9 +182.5 +185.6 +177.4 +191.4 +183.8 +177.0 +181.2 +176.3 +177.3 +173.7 +177.5 +178.9 +178.2 +177.3 +177.7 +187.8 +181.6 +178.4 +176.4 +180.8 +178.5 +189.5 +186.0 +188.4 +182.2 +184.0 +176.5 +179.5 +185.3 +173.8 +190.0 +180.2 +189.1 +184.9 +175.8 +182.2 +177.9 +176.4 +184.1 +179.5 +181.8 +175.7 +179.7 +178.7 +185.9 +180.5 +180.9 +175.5 +176.1 +174.4 +175.1 +195.2 +174.9 +190.5 +183.8 +188.7 +182.5 +179.5 +177.0 +174.9 +185.6 +180.8 +192.8 +185.2 +184.0 +177.7 +191.7 +185.3 +185.4 +186.6 +182.9 +181.1 +191.2 +183.0 +176.6 +187.7 +181.9 +179.5 +178.7 +176.9 +178.5 +182.1 +175.4 +175.9 +183.8 +180.6 +191.1 +177.0 +184.6 +188.7 +176.0 +181.4 +188.2 +181.1 +180.0 +178.9 +182.1 +179.6 +179.5 +174.5 +177.6 +179.9 +175.3 +185.2 +175.4 +185.1 +184.9 +182.0 +178.4 +177.0 +182.3 +193.8 +181.6 +197.4 +180.4 +180.9 +182.0 +181.0 +185.8 +178.5 +180.5 +182.3 +175.7 +176.5 +182.8 +176.8 +178.3 +179.5 +181.8 +175.1 +180.2 +179.6 +176.7 +174.6 +180.5 +179.7 +178.9 +177.1 +184.2 +178.4 +175.0 +183.7 +178.3 +179.1 +175.3 +187.5 +189.2 +176.7 +205.6 +179.6 +179.6 +179.1 +182.1 +178.6 +182.2 +181.6 +181.9 +188.8 +184.7 +185.3 +182.5 +174.2 +176.5 +190.1 +180.6 +182.7 +182.2 +179.5 +179.0 +184.4 +175.4 +178.2 +174.6 +179.0 +188.4 +175.9 +173.8 +180.2 +187.3 +186.3 +176.3 +174.9 +178.6 +178.2 +177.0 +180.0 +177.2 +173.5 +195.4 +180.5 +181.6 +177.2 +176.7 +175.3 +174.8 +179.7 +175.0 +176.4 +181.8 +183.0 +180.0 +177.0 +185.8 +183.2 +181.0 +176.8 +177.9 +186.1 +182.5 +174.4 +177.1 +176.7 +176.3 +180.3 +178.9 +178.3 +175.8 +179.9 +190.6 +182.8 +183.4 +177.9 +177.7 +180.0 +202.0 +184.5 +179.5 +181.5 +174.8 +194.2 +185.8 +177.0 +183.9 +177.6 +202.4 +179.9 +175.4 +174.8 +176.7 +177.9 +176.6 +176.5 +181.2 +181.3 +177.4 +179.3 +177.8 +176.2 +197.2 +180.9 +178.2 +184.6 +184.7 +191.0 +182.9 +179.9 +180.3 +177.4 +182.1 +181.9 +182.2 +174.9 +193.4 +175.1 +203.2 +176.0 +180.3 +180.0 +186.3 +174.8 +177.9 +196.1 +177.6 +183.5 +176.2 +187.6 +178.5 +180.6 +178.2 +180.7 +177.6 +176.6 +173.5 +178.7 +175.0 +179.0 +175.5 +174.2 +178.2 +176.6 +176.3 +179.4 +178.6 +175.2 +175.9 +175.0 +179.5 +176.9 +184.0 +184.6 +179.9 +175.3 +176.2 +179.1 +182.8 +180.5 +173.4 +176.0 +198.9 +177.0 +189.7 +183.6 +179.5 +176.5 +175.5 +175.5 +176.4 +182.9 +178.7 +177.5 +180.6 +175.0 +178.9 +177.7 +178.0 +178.6 +176.0 +177.6 +180.8 +179.6 +184.3 +190.2 +177.7 +178.1 +175.0 +178.6 +179.6 +185.1 +193.7 +179.8 +179.3 +178.6 +180.4 +174.9 +177.8 +187.4 +183.7 +181.6 +177.7 +185.4 +180.6 +178.5 +182.8 +179.9 +177.8 +178.0 +184.3 +191.0 +181.1 +197.9 +181.1 +197.8 +175.6 +176.9 +177.9 +187.4 +180.8 +180.6 +174.9 +180.8 +175.7 +176.4 +179.9 +178.3 +178.3 +178.4 +196.4 +177.3 +184.3 +177.3 +176.5 +181.0 +175.0 +179.2 +178.1 +183.3 +177.0 +175.2 +177.2 +173.5 +176.5 +177.0 +178.6 +181.3 +189.2 +186.4 +177.4 +181.7 +176.2 +194.1 +177.9 +186.6 +182.6 +176.6 +175.4 +250.3 +178.7 +183.0 +182.7 +183.1 +183.6 +178.8 +178.3 +179.1 +177.7 +177.9 +186.2 +195.4 +179.2 +183.3 +183.8 +186.3 +182.7 +178.9 +196.0 +177.1 +184.5 +188.3 +188.4 +181.5 +190.1 +179.0 +177.2 +189.1 +184.1 +184.2 +178.7 +187.4 +182.5 +189.8 +181.1 +186.0 +178.8 +178.0 +176.9 +180.6 +174.3 +180.4 +176.7 +175.5 +193.7 +189.7 +196.6 +178.9 +186.3 +182.5 +185.7 +175.7 +176.0 +194.1 +181.0 +178.6 +178.4 +186.8 +175.6 +185.4 +180.7 +184.7 +188.1 +180.4 +183.0 +178.4 +185.1 +181.8 +175.8 +178.9 +177.9 +177.9 +176.5 +290.3 +181.8 +180.8 +183.9 +190.8 +179.0 +183.7 +181.9 +177.5 +178.4 +179.4 +179.9 +174.2 +182.7 +182.9 +178.8 +179.2 +183.2 +181.8 +175.6 +179.0 +175.7 +178.1 +179.2 +183.8 +187.8 +187.3 +175.7 +175.0 +185.5 +175.3 +181.9 +176.7 +184.6 +185.8 +181.1 +186.6 +178.8 +183.4 +175.1 +178.4 +179.6 +176.7 +177.1 +176.5 +181.7 +199.1 +194.8 +176.5 +182.2 +189.9 +177.6 +179.3 +184.9 +178.1 +176.4 +184.6 +174.6 +188.6 +183.7 +178.1 +177.7 +186.1 +176.3 +180.9 +180.3 +181.0 +180.0 +180.0 +180.4 +174.8 +179.5 +176.5 +179.9 +175.6 +184.0 +176.4 +191.6 +178.4 +184.8 +186.7 +197.0 diff --git a/netem/experimental.dist b/netem/experimental.dist new file mode 100644 index 0000000..025c198 --- /dev/null +++ b/netem/experimental.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the experimental distribution. +-10576 -10197 -10134 -10071 -10008 -9945 -9882 -9819 +-9788 -9756 -9725 -9693 -9672 -9651 -9630 -9615 +-9599 -9583 -9567 -9546 -9525 -9504 -9483 -9462 +-9441 -9420 -9399 -9378 -9366 -9353 -9341 -9328 +-9315 -9303 -9290 -9278 -9265 -9252 -9245 -9237 +-9229 -9221 -9213 -9205 -9197 -9189 -9183 -9177 +-9171 -9164 -9158 -9152 -9145 -9139 -9133 -9126 +-9119 -9111 -9103 -9095 -9087 -9079 -9071 -9063 +-9054 -9045 -9036 -9027 -9018 -9009 -9000 -8993 +-8986 -8979 -8972 -8965 -8958 -8951 -8944 -8937 +-8932 -8927 -8922 -8916 -8911 -8906 -8901 -8895 +-8890 -8885 -8880 -8874 -8867 -8859 -8851 -8843 +-8835 -8827 -8819 -8811 -8804 -8797 -8790 -8783 +-8776 -8769 -8762 -8755 -8748 -8744 -8740 -8736 +-8732 -8727 -8723 -8719 -8715 -8711 -8706 -8702 +-8698 -8694 -8690 -8685 -8681 -8676 -8671 -8666 +-8661 -8656 -8652 -8647 -8642 -8637 -8632 -8627 +-8622 -8618 -8613 -8609 -8604 -8600 -8595 -8591 +-8586 -8582 -8577 -8573 -8568 -8564 -8559 -8556 +-8552 -8548 -8545 -8541 -8537 -8534 -8530 -8526 +-8522 -8519 -8515 -8511 -8508 -8504 -8500 -8496 +-8492 -8487 -8482 -8477 -8472 -8467 -8463 -8458 +-8453 -8448 -8443 -8438 -8433 -8429 -8425 -8421 +-8416 -8412 -8408 -8404 -8399 -8395 -8391 -8387 +-8382 -8378 -8374 -8369 -8366 -8362 -8358 -8354 +-8350 -8346 -8342 -8338 -8334 -8330 -8326 -8322 +-8318 -8314 -8310 -8306 -8303 -8299 -8295 -8292 +-8288 -8284 -8281 -8277 -8273 -8269 -8266 -8262 +-8258 -8255 -8251 -8247 -8243 -8240 -8236 -8232 +-8229 -8225 -8221 -8218 -8214 -8210 -8206 -8203 +-8199 -8195 -8192 -8188 -8184 -8180 -8176 -8172 +-8168 -8164 -8159 -8155 -8151 -8147 -8143 -8138 +-8134 -8130 -8126 -8122 -8117 -8114 -8111 -8108 +-8105 -8102 -8099 -8095 -8092 -8089 -8086 -8083 +-8080 -8077 -8073 -8070 -8067 -8064 -8061 -8058 +-8054 -8051 -8047 -8043 -8040 -8036 -8032 -8029 +-8025 -8021 -8017 -8014 -8010 -8006 -8003 -7999 +-7995 -7991 -7988 -7984 -7981 -7977 -7974 -7970 +-7967 -7963 -7960 -7956 -7953 -7949 -7946 -7942 +-7939 -7935 -7932 -7928 -7925 -7921 -7917 -7914 +-7910 -7906 -7903 -7899 -7895 -7891 -7888 -7884 +-7880 -7877 -7873 -7869 -7865 -7861 -7857 -7853 +-7849 -7844 -7840 -7836 -7832 -7828 -7823 -7819 +-7815 -7811 -7807 -7802 -7800 -7797 -7794 -7792 +-7789 -7786 -7783 -7781 -7778 -7775 -7772 -7770 +-7767 -7764 -7761 -7759 -7756 -7753 -7750 -7748 +-7745 -7742 -7739 -7736 -7733 -7730 -7727 -7724 +-7721 -7717 -7714 -7711 -7708 -7705 -7702 -7699 +-7695 -7692 -7689 -7686 -7683 -7680 -7676 -7674 +-7671 -7668 -7665 -7662 -7659 -7656 -7654 -7651 +-7648 -7645 -7642 -7639 -7636 -7634 -7631 -7628 +-7625 -7622 -7619 -7616 -7613 -7611 -7609 -7606 +-7604 -7601 -7599 -7597 -7594 -7592 -7589 -7587 +-7584 -7582 -7580 -7577 -7575 -7572 -7570 -7567 +-7565 -7563 -7560 -7558 -7555 -7553 -7550 -7547 +-7544 -7541 -7537 -7534 -7531 -7527 -7524 -7521 +-7517 -7514 -7511 -7507 -7504 -7501 -7497 -7494 +-7491 -7487 -7484 -7480 -7477 -7473 -7470 -7466 +-7463 -7459 -7456 -7452 -7449 -7445 -7442 -7438 +-7435 -7431 -7428 -7424 -7420 -7416 -7412 -7408 +-7403 -7399 -7395 -7391 -7387 -7382 -7378 -7374 +-7370 -7366 -7361 -7359 -7357 -7354 -7352 -7349 +-7347 -7345 -7342 -7340 -7337 -7335 -7332 -7330 +-7328 -7325 -7323 -7320 -7318 -7315 -7313 -7311 +-7308 -7306 -7303 -7301 -7298 -7296 -7294 -7291 +-7289 -7286 -7284 -7282 -7279 -7277 -7274 -7272 +-7269 -7267 -7265 -7262 -7260 -7257 -7255 -7252 +-7250 -7248 -7245 -7243 -7240 -7238 -7235 -7232 +-7229 -7226 -7222 -7219 -7216 -7212 -7209 -7206 +-7202 -7199 -7196 -7192 -7189 -7186 -7182 -7179 +-7176 -7172 -7169 -7165 -7162 -7158 -7155 -7151 +-7148 -7144 -7141 -7137 -7134 -7130 -7127 -7123 +-7120 -7116 -7113 -7109 -7107 -7104 -7101 -7098 +-7095 -7092 -7089 -7087 -7084 -7081 -7078 -7075 +-7072 -7069 -7067 -7064 -7061 -7058 -7055 -7052 +-7049 -7046 -7043 -7040 -7037 -7034 -7031 -7028 +-7025 -7022 -7019 -7016 -7013 -7010 -7007 -7004 +-7001 -6998 -6995 -6992 -6989 -6986 -6983 -6980 +-6977 -6974 -6970 -6967 -6964 -6960 -6957 -6954 +-6950 -6947 -6944 -6940 -6937 -6934 -6930 -6927 +-6924 -6920 -6918 -6915 -6912 -6909 -6906 -6903 +-6900 -6898 -6895 -6892 -6889 -6886 -6883 -6880 +-6878 -6875 -6872 -6869 -6866 -6863 -6860 -6857 +-6855 -6852 -6850 -6847 -6844 -6842 -6839 -6836 +-6834 -6831 -6829 -6826 -6823 -6821 -6818 -6815 +-6813 -6810 -6808 -6805 -6802 -6800 -6797 -6794 +-6792 -6789 -6786 -6783 -6780 -6777 -6774 -6772 +-6769 -6766 -6763 -6760 -6757 -6754 -6752 -6749 +-6746 -6743 -6740 -6737 -6734 -6731 -6729 -6727 +-6725 -6722 -6720 -6718 -6715 -6713 -6711 -6709 +-6706 -6704 -6702 -6699 -6697 -6695 -6693 -6690 +-6688 -6686 -6683 -6681 -6679 -6677 -6674 -6672 +-6670 -6667 -6665 -6662 -6659 -6656 -6653 -6651 +-6648 -6645 -6642 -6639 -6636 -6634 -6631 -6628 +-6625 -6622 -6620 -6617 -6614 -6611 -6608 -6605 +-6602 -6599 -6596 -6593 -6589 -6586 -6583 -6580 +-6577 -6573 -6570 -6567 -6564 -6561 -6557 -6554 +-6551 -6548 -6545 -6541 -6539 -6537 -6534 -6532 +-6530 -6527 -6525 -6523 -6520 -6518 -6516 -6513 +-6511 -6509 -6506 -6504 -6502 -6499 -6497 -6495 +-6492 -6490 -6488 -6485 -6483 -6481 -6478 -6475 +-6472 -6469 -6465 -6462 -6459 -6455 -6452 -6449 +-6445 -6442 -6439 -6435 -6432 -6429 -6425 -6422 +-6419 -6415 -6412 -6408 -6405 -6401 -6398 -6394 +-6391 -6387 -6384 -6380 -6377 -6373 -6370 -6366 +-6363 -6359 -6356 -6352 -6349 -6346 -6343 -6340 +-6337 -6334 -6331 -6328 -6325 -6322 -6319 -6316 +-6313 -6310 -6307 -6304 -6301 -6298 -6295 -6292 +-6289 -6287 -6284 -6282 -6279 -6276 -6274 -6271 +-6268 -6266 -6263 -6261 -6258 -6255 -6253 -6250 +-6247 -6245 -6242 -6240 -6237 -6234 -6232 -6229 +-6226 -6223 -6219 -6216 -6212 -6209 -6205 -6202 +-6198 -6195 -6191 -6188 -6184 -6181 -6177 -6174 +-6170 -6167 -6163 -6160 -6156 -6152 -6149 -6145 +-6141 -6138 -6134 -6130 -6126 -6123 -6119 -6115 +-6112 -6108 -6104 -6100 -6098 -6095 -6092 -6089 +-6086 -6083 -6080 -6078 -6075 -6072 -6069 -6066 +-6063 -6060 -6058 -6055 -6052 -6049 -6046 -6043 +-6040 -6037 -6035 -6032 -6029 -6027 -6024 -6021 +-6018 -6016 -6013 -6010 -6007 -6005 -6002 -5999 +-5996 -5994 -5991 -5988 -5985 -5983 -5980 -5977 +-5974 -5971 -5968 -5965 -5962 -5959 -5956 -5952 +-5949 -5946 -5943 -5940 -5937 -5934 -5930 -5927 +-5924 -5921 -5918 -5915 -5911 -5909 -5906 -5903 +-5900 -5897 -5894 -5891 -5889 -5886 -5883 -5880 +-5877 -5874 -5871 -5869 -5866 -5863 -5860 -5857 +-5854 -5851 -5848 -5846 -5843 -5840 -5837 -5834 +-5831 -5828 -5826 -5823 -5820 -5817 -5814 -5811 +-5808 -5806 -5803 -5800 -5797 -5794 -5791 -5788 +-5785 -5782 -5779 -5776 -5772 -5769 -5766 -5762 +-5759 -5756 -5752 -5749 -5746 -5742 -5739 -5736 +-5732 -5729 -5726 -5722 -5720 -5717 -5714 -5711 +-5708 -5705 -5702 -5700 -5697 -5694 -5691 -5688 +-5685 -5682 -5680 -5677 -5674 -5671 -5668 -5665 +-5662 -5659 -5657 -5655 -5652 -5650 -5647 -5645 +-5643 -5640 -5638 -5635 -5633 -5630 -5628 -5626 +-5623 -5621 -5618 -5616 -5613 -5611 -5609 -5606 +-5604 -5601 -5599 -5596 -5592 -5587 -5583 -5578 +-5574 -5569 -5565 -5560 -5556 -5551 -5547 -5542 +-5538 -5533 -5531 -5528 -5526 -5523 -5521 -5518 +-5516 -5513 -5511 -5508 -5506 -5503 -5501 -5498 +-5496 -5493 -5491 -5488 -5486 -5483 -5481 -5478 +-5476 -5473 -5470 -5467 -5464 -5461 -5457 -5454 +-5451 -5447 -5444 -5441 -5437 -5434 -5431 -5427 +-5424 -5421 -5417 -5414 -5411 -5407 -5404 -5401 +-5398 -5394 -5391 -5388 -5384 -5381 -5378 -5374 +-5371 -5368 -5364 -5361 -5358 -5354 -5351 -5348 +-5344 -5341 -5338 -5335 -5331 -5328 -5325 -5321 +-5318 -5315 -5311 -5308 -5305 -5301 -5298 -5295 +-5291 -5288 -5285 -5281 -5278 -5274 -5270 -5267 +-5263 -5259 -5256 -5252 -5248 -5244 -5241 -5237 +-5233 -5230 -5226 -5222 -5218 -5216 -5214 -5212 +-5210 -5208 -5205 -5203 -5201 -5199 -5197 -5195 +-5192 -5190 -5188 -5186 -5184 -5182 -5179 -5177 +-5175 -5173 -5171 -5169 -5166 -5164 -5162 -5160 +-5158 -5155 -5152 -5148 -5144 -5140 -5136 -5132 +-5128 -5124 -5120 -5116 -5112 -5108 -5104 -5100 +-5096 -5092 -5089 -5086 -5083 -5079 -5076 -5073 +-5069 -5066 -5063 -5059 -5056 -5053 -5049 -5046 +-5043 -5039 -5036 -5033 -5029 -5027 -5024 -5022 +-5019 -5017 -5014 -5012 -5009 -5007 -5004 -5002 +-4999 -4997 -4994 -4992 -4989 -4987 -4984 -4982 +-4979 -4977 -4974 -4972 -4969 -4966 -4963 -4960 +-4957 -4954 -4951 -4948 -4944 -4941 -4938 -4935 +-4932 -4929 -4926 -4922 -4919 -4916 -4913 -4910 +-4907 -4903 -4899 -4895 -4891 -4887 -4882 -4878 +-4874 -4870 -4866 -4861 -4857 -4853 -4849 -4845 +-4840 -4837 -4833 -4829 -4825 -4822 -4818 -4814 +-4810 -4807 -4803 -4799 -4795 -4792 -4788 -4784 +-4780 -4776 -4774 -4771 -4768 -4765 -4762 -4759 +-4756 -4753 -4750 -4747 -4744 -4741 -4738 -4735 +-4732 -4729 -4726 -4723 -4720 -4717 -4714 -4711 +-4708 -4705 -4702 -4699 -4696 -4693 -4690 -4687 +-4684 -4681 -4678 -4675 -4672 -4669 -4666 -4663 +-4660 -4657 -4654 -4650 -4647 -4643 -4639 -4636 +-4632 -4628 -4625 -4621 -4617 -4613 -4610 -4606 +-4602 -4599 -4595 -4591 -4587 -4584 -4580 -4576 +-4572 -4568 -4564 -4560 -4556 -4552 -4548 -4544 +-4540 -4536 -4532 -4528 -4524 -4520 -4516 -4512 +-4508 -4503 -4499 -4495 -4491 -4487 -4482 -4478 +-4474 -4470 -4466 -4461 -4459 -4456 -4453 -4450 +-4447 -4444 -4441 -4439 -4436 -4433 -4430 -4427 +-4424 -4421 -4419 -4416 -4413 -4410 -4407 -4404 +-4401 -4398 -4395 -4391 -4388 -4384 -4381 -4377 +-4374 -4370 -4367 -4363 -4360 -4356 -4353 -4349 +-4346 -4342 -4339 -4335 -4332 -4329 -4326 -4323 +-4320 -4317 -4314 -4311 -4308 -4305 -4302 -4299 +-4296 -4293 -4290 -4287 -4284 -4281 -4278 -4275 +-4272 -4268 -4263 -4258 -4253 -4248 -4243 -4239 +-4234 -4229 -4224 -4219 -4214 -4209 -4206 -4202 +-4199 -4195 -4192 -4188 -4185 -4181 -4178 -4174 +-4171 -4167 -4164 -4160 -4157 -4153 -4150 -4146 +-4142 -4138 -4134 -4130 -4125 -4121 -4117 -4113 +-4109 -4104 -4100 -4096 -4092 -4088 -4083 -4080 +-4076 -4073 -4069 -4066 -4062 -4059 -4055 -4052 +-4048 -4045 -4041 -4038 -4034 -4031 -4027 -4024 +-4020 -4017 -4013 -4010 -4006 -4003 -3999 -3996 +-3992 -3989 -3985 -3982 -3978 -3975 -3971 -3968 +-3964 -3961 -3957 -3954 -3950 -3947 -3943 -3940 +-3936 -3933 -3929 -3926 -3922 -3919 -3915 -3912 +-3908 -3905 -3901 -3898 -3894 -3891 -3887 -3884 +-3880 -3877 -3873 -3870 -3866 -3863 -3859 -3856 +-3852 -3849 -3845 -3842 -3838 -3835 -3831 -3827 +-3823 -3819 -3815 -3810 -3806 -3802 -3798 -3794 +-3789 -3785 -3781 -3777 -3773 -3768 -3765 -3761 +-3757 -3754 -3750 -3746 -3743 -3739 -3735 -3731 +-3728 -3724 -3720 -3717 -3713 -3709 -3705 -3702 +-3699 -3696 -3692 -3689 -3686 -3682 -3679 -3676 +-3672 -3669 -3666 -3662 -3659 -3656 -3652 -3649 +-3646 -3642 -3639 -3635 -3631 -3628 -3624 -3620 +-3617 -3613 -3609 -3605 -3602 -3598 -3594 -3591 +-3587 -3583 -3579 -3575 -3570 -3566 -3561 -3557 +-3552 -3548 -3543 -3539 -3534 -3530 -3525 -3521 +-3516 -3513 -3509 -3505 -3501 -3497 -3493 -3489 +-3485 -3481 -3477 -3473 -3469 -3465 -3461 -3457 +-3453 -3450 -3446 -3442 -3439 -3435 -3431 -3428 +-3424 -3420 -3416 -3413 -3409 -3405 -3402 -3398 +-3394 -3390 -3387 -3383 -3380 -3376 -3373 -3369 +-3366 -3362 -3359 -3355 -3352 -3348 -3345 -3341 +-3338 -3334 -3331 -3327 -3323 -3318 -3314 -3309 +-3305 -3300 -3296 -3291 -3287 -3282 -3278 -3273 +-3269 -3264 -3260 -3255 -3250 -3245 -3240 -3235 +-3231 -3226 -3221 -3216 -3211 -3206 -3201 -3198 +-3194 -3190 -3186 -3182 -3178 -3174 -3170 -3166 +-3162 -3158 -3154 -3150 -3146 -3142 -3138 -3135 +-3131 -3128 -3124 -3121 -3117 -3114 -3110 -3107 +-3103 -3100 -3096 -3093 -3089 -3086 -3082 -3079 +-3075 -3072 -3068 -3065 -3061 -3058 -3054 -3051 +-3047 -3044 -3040 -3037 -3033 -3030 -3026 -3023 +-3019 -3016 -3012 -3008 -3004 -3000 -2995 -2991 +-2987 -2983 -2978 -2974 -2970 -2966 -2961 -2957 +-2953 -2948 -2945 -2942 -2939 -2935 -2932 -2929 +-2925 -2922 -2919 -2915 -2912 -2909 -2905 -2902 +-2899 -2895 -2892 -2889 -2885 -2882 -2878 -2874 +-2870 -2866 -2862 -2858 -2854 -2850 -2846 -2842 +-2838 -2834 -2830 -2826 -2822 -2819 -2815 -2811 +-2807 -2803 -2799 -2795 -2791 -2787 -2783 -2779 +-2775 -2771 -2767 -2763 -2759 -2755 -2750 -2745 +-2740 -2735 -2730 -2726 -2721 -2716 -2711 -2706 +-2701 -2696 -2692 -2688 -2684 -2680 -2675 -2671 +-2667 -2663 -2659 -2654 -2650 -2646 -2642 -2638 +-2633 -2630 -2626 -2622 -2618 -2614 -2610 -2606 +-2602 -2598 -2594 -2590 -2586 -2582 -2578 -2574 +-2570 -2566 -2561 -2557 -2552 -2548 -2543 -2539 +-2534 -2530 -2525 -2521 -2516 -2512 -2507 -2502 +-2497 -2492 -2486 -2481 -2476 -2471 -2465 -2460 +-2455 -2450 -2444 -2439 -2434 -2429 -2423 -2418 +-2413 -2408 -2402 -2397 -2392 -2387 -2381 -2376 +-2371 -2366 -2360 -2355 -2350 -2345 -2339 -2334 +-2329 -2324 -2318 -2314 -2309 -2305 -2300 -2296 +-2291 -2287 -2282 -2278 -2273 -2269 -2264 -2260 +-2255 -2251 -2246 -2242 -2237 -2233 -2228 -2224 +-2219 -2215 -2210 -2206 -2201 -2197 -2192 -2188 +-2183 -2179 -2174 -2170 -2165 -2161 -2156 -2152 +-2147 -2143 -2138 -2134 -2129 -2125 -2121 -2117 +-2113 -2108 -2104 -2100 -2096 -2092 -2087 -2083 +-2079 -2075 -2071 -2066 -2062 -2057 -2052 -2047 +-2042 -2037 -2033 -2028 -2023 -2018 -2013 -2008 +-2003 -1999 -1994 -1989 -1984 -1979 -1974 -1970 +-1965 -1960 -1955 -1950 -1945 -1940 -1936 -1931 +-1926 -1921 -1916 -1911 -1907 -1902 -1897 -1892 +-1887 -1882 -1877 -1873 -1869 -1865 -1861 -1856 +-1852 -1848 -1844 -1840 -1835 -1831 -1827 -1823 +-1819 -1814 -1811 -1807 -1803 -1800 -1796 -1792 +-1789 -1785 -1781 -1777 -1774 -1770 -1766 -1763 +-1759 -1755 -1751 -1747 -1742 -1737 -1732 -1727 +-1722 -1718 -1713 -1708 -1703 -1698 -1693 -1688 +-1684 -1679 -1674 -1669 -1664 -1659 -1655 -1650 +-1645 -1640 -1635 -1630 -1625 -1620 -1615 -1610 +-1604 -1599 -1594 -1589 -1583 -1578 -1573 -1568 +-1562 -1557 -1552 -1547 -1541 -1536 -1531 -1526 +-1520 -1515 -1510 -1505 -1499 -1495 -1490 -1485 +-1480 -1475 -1470 -1466 -1461 -1456 -1451 -1446 +-1441 -1436 -1431 -1426 -1421 -1415 -1410 -1405 +-1400 -1394 -1389 -1384 -1379 -1373 -1369 -1364 +-1360 -1355 -1351 -1346 -1342 -1337 -1333 -1328 +-1324 -1319 -1315 -1310 -1307 -1303 -1299 -1295 +-1292 -1288 -1284 -1280 -1277 -1273 -1269 -1265 +-1262 -1258 -1254 -1250 -1246 -1242 -1238 -1233 +-1229 -1224 -1220 -1215 -1211 -1207 -1202 -1198 +-1193 -1189 -1184 -1180 -1175 -1171 -1166 -1162 +-1157 -1153 -1148 -1144 -1139 -1135 -1130 -1126 +-1121 -1116 -1110 -1104 -1098 -1092 -1087 -1081 +-1075 -1069 -1063 -1057 -1052 -1047 -1042 -1036 +-1031 -1026 -1021 -1015 -1010 -1005 -1000 -994 +-990 -985 -981 -976 -972 -967 -963 -958 +-954 -949 -945 -940 -936 -931 -927 -923 +-919 -915 -910 -906 -902 -898 -894 -889 +-885 -881 -877 -873 -868 -861 -854 -847 +-840 -833 -826 -819 -812 -805 -800 -794 +-788 -783 -777 -771 -765 -760 -754 -748 +-742 -737 -731 -725 -720 -714 -708 -702 +-697 -691 -685 -679 -675 -670 -666 -661 +-657 -652 -648 -643 -639 -634 -630 -625 +-621 -616 -611 -605 -599 -594 -588 -582 +-576 -571 -565 -559 -553 -548 -542 -536 +-531 -525 -519 -513 -508 -502 -496 -490 +-485 -480 -475 -469 -464 -459 -454 -448 +-443 -438 -433 -427 -420 -412 -404 -396 +-388 -380 -372 -364 -355 -346 -337 -328 +-319 -310 -301 -294 -287 -280 -273 -266 +-259 -252 -245 -238 -234 -229 -224 -219 +-214 -209 -205 -200 -195 -190 -185 -180 +-175 -168 -161 -154 -147 -140 -133 -126 +-119 -112 -107 -101 -95 -90 -84 -78 +-72 -67 -61 -55 -49 -43 -37 -31 +-24 -18 -12 -5 1 7 14 21 +28 35 42 49 56 63 70 77 +83 89 95 102 108 114 121 127 +133 140 147 154 161 168 175 182 +189 196 203 210 217 224 231 238 +245 252 259 266 272 278 284 291 +297 303 310 316 322 329 334 339 +344 350 355 360 365 371 376 381 +386 392 399 406 413 420 427 434 +441 448 455 462 470 478 486 494 +502 510 518 523 529 535 540 546 +552 558 563 569 575 581 590 599 +608 617 626 635 645 652 659 666 +673 680 687 694 701 708 714 720 +726 733 739 745 752 758 764 771 +775 780 784 789 793 798 802 807 +811 816 820 825 829 834 839 845 +851 856 862 868 874 879 885 891 +897 902 907 912 918 923 928 933 +939 944 949 954 960 969 978 987 +996 1005 1014 1023 1033 1044 1054 1065 +1075 1086 1093 1101 1109 1117 1125 1133 +1141 1149 1158 1167 1176 1185 1194 1203 +1212 1218 1224 1230 1237 1243 1249 1256 +1262 1268 1275 1281 1287 1293 1300 1306 +1312 1319 1325 1331 1338 1347 1356 1365 +1374 1383 1392 1401 1406 1412 1418 1423 +1429 1435 1441 1446 1452 1458 1464 1469 +1475 1481 1486 1492 1498 1504 1509 1515 +1521 1527 1536 1545 1554 1563 1572 1581 +1590 1599 1608 1617 1626 1635 1644 1653 +1660 1667 1674 1681 1688 1695 1702 1709 +1716 1722 1728 1734 1741 1747 1753 1760 +1766 1772 1779 1789 1800 1810 1821 1831 +1842 1847 1853 1859 1864 1870 1876 1882 +1887 1893 1899 1905 1915 1926 1936 1947 +1957 1968 1975 1983 1991 1999 2007 2015 +2023 2031 2038 2046 2054 2062 2070 2078 +2086 2094 2101 2108 2115 2122 2129 2136 +2143 2150 2157 2164 2172 2180 2188 2196 +2204 2212 2220 2225 2231 2237 2242 2248 +2254 2260 2265 2271 2277 2283 2292 2301 +2310 2319 2328 2337 2347 2352 2358 2363 +2369 2375 2380 2386 2392 2397 2403 2409 +2417 2425 2433 2441 2449 2457 2465 2473 +2480 2488 2496 2504 2512 2520 2528 2536 +2542 2548 2554 2561 2567 2573 2580 2586 +2592 2599 2611 2624 2636 2649 2662 2669 +2676 2683 2690 2697 2704 2711 2718 2725 +2732 2740 2748 2756 2764 2772 2780 2788 +2795 2803 2811 2819 2827 2835 2843 2851 +2857 2863 2869 2876 2882 2888 2895 2901 +2907 2914 2921 2929 2937 2945 2953 2961 +2969 2977 2986 2995 3004 3013 3022 3031 +3040 3050 3061 3071 3082 3092 3103 3110 +3117 3124 3131 3138 3145 3152 3159 3166 +3173 3180 3187 3194 3201 3208 3215 3222 +3229 3235 3241 3247 3254 3260 3266 3273 +3279 3285 3292 3298 3304 3310 3317 3323 +3329 3336 3342 3348 3355 3361 3367 3373 +3380 3386 3392 3399 3405 3411 3418 3427 +3436 3445 3454 3463 3472 3481 3491 3502 +3512 3523 3533 3544 3551 3559 3567 3575 +3583 3591 3599 3607 3619 3632 3644 3657 +3670 3676 3682 3688 3695 3701 3707 3714 +3720 3726 3733 3738 3744 3750 3755 3761 +3767 3773 3778 3784 3790 3796 3803 3811 +3819 3827 3835 3843 3851 3859 3866 3874 +3882 3890 3898 3906 3914 3922 3932 3943 +3953 3964 3974 3985 3992 3999 4006 4013 +4020 4027 4034 4041 4048 4058 4069 4079 +4090 4100 4111 4120 4129 4138 4147 4156 +4165 4174 4180 4186 4193 4199 4206 4212 +4218 4225 4231 4238 4245 4253 4261 4269 +4276 4284 4292 4300 4308 4316 4324 4332 +4340 4348 4356 4364 4371 4379 4387 4395 +4403 4411 4419 4427 4434 4441 4448 4455 +4462 4469 4476 4483 4490 4505 4521 4537 +4553 4560 4567 4574 4581 4588 4595 4602 +4609 4616 4625 4634 4643 4652 4661 4670 +4679 4688 4697 4706 4715 4724 4733 4742 +4751 4760 4769 4778 4787 4796 4805 4812 +4819 4826 4833 4840 4847 4854 4861 4868 +4875 4882 4889 4896 4903 4910 4917 4924 +4931 4941 4952 4962 4973 4983 4994 5004 +5015 5025 5036 5046 5057 5064 5071 5078 +5085 5092 5099 5106 5113 5120 5129 5138 +5147 5156 5165 5174 5183 5193 5204 5214 +5225 5235 5246 5258 5271 5283 5296 5309 +5318 5327 5336 5345 5354 5363 5372 5382 +5393 5403 5414 5424 5435 5445 5456 5466 +5477 5487 5498 5505 5513 5521 5529 5537 +5545 5553 5561 5568 5576 5584 5592 5600 +5608 5616 5624 5634 5645 5655 5666 5676 +5687 5696 5705 5714 5723 5732 5741 5750 +5757 5764 5771 5778 5785 5792 5799 5806 +5813 5818 5824 5830 5835 5841 5847 5853 +5858 5864 5870 5876 5886 5897 5907 5918 +5928 5939 5951 5964 5976 5989 6002 6011 +6020 6029 6038 6047 6056 6066 6075 6084 +6093 6102 6111 6120 6129 6138 6147 6156 +6165 6174 6183 6192 6199 6207 6215 6223 +6231 6239 6247 6255 6262 6269 6276 6283 +6290 6297 6304 6311 6318 6325 6333 6341 +6349 6357 6365 6373 6381 6391 6402 6412 +6423 6433 6444 6453 6462 6471 6480 6489 +6498 6507 6514 6521 6528 6535 6542 6549 +6556 6563 6570 6579 6588 6597 6606 6615 +6624 6633 6640 6648 6656 6664 6672 6680 +6688 6696 6705 6714 6723 6732 6741 6750 +6759 6771 6784 6796 6809 6822 6837 6853 +6869 6885 6892 6900 6908 6916 6924 6932 +6940 6948 6957 6966 6975 6984 6993 7002 +7011 7021 7032 7042 7053 7063 7074 7083 +7092 7101 7110 7119 7128 7137 7142 7148 +7154 7159 7165 7171 7177 7182 7188 7194 +7200 7210 7221 7231 7242 7252 7263 7269 +7275 7281 7288 7294 7300 7307 7313 7319 +7326 7341 7357 7373 7389 7399 7410 7420 +7431 7441 7452 7459 7466 7473 7480 7487 +7494 7501 7508 7515 7522 7530 7538 7546 +7554 7562 7570 7578 7588 7599 7609 7620 +7630 7641 7651 7662 7672 7683 7693 7704 +7714 7725 7735 7746 7756 7767 7777 7788 +7798 7809 7819 7830 7839 7848 7857 7866 +7875 7884 7893 7902 7911 7920 7929 7938 +7947 7957 7966 7975 7984 7993 8002 8011 +8020 8030 8041 8051 8062 8072 8083 8090 +8098 8106 8114 8122 8130 8138 8146 8158 +8171 8183 8196 8209 8219 8230 8240 8251 +8261 8272 8281 8290 8299 8308 8317 8326 +8335 8345 8356 8366 8377 8387 8398 8413 +8429 8445 8461 8471 8482 8492 8503 8513 +8524 8531 8539 8547 8555 8563 8571 8579 +8587 8597 8608 8618 8629 8639 8650 8657 +8665 8673 8681 8689 8697 8705 8713 8722 +8731 8740 8749 8758 8767 8776 8783 8790 +8797 8804 8811 8818 8825 8832 8839 8849 +8860 8870 8881 8891 8902 8914 8927 8939 +8952 8965 8974 8983 8992 9001 9010 9019 +9028 9038 9049 9059 9070 9080 9091 9101 +9112 9122 9133 9143 9154 9166 9179 9191 +9204 9217 9223 9229 9235 9242 9248 9254 +9261 9267 9273 9280 9290 9301 9311 9322 +9332 9343 9353 9364 9374 9385 9395 9406 +9421 9437 9453 9469 9481 9494 9506 9519 +9532 9542 9553 9563 9574 9584 9595 9607 +9620 9633 9646 9659 9669 9679 9690 9700 +9710 9721 9733 9746 9759 9772 9785 9795 +9806 9816 9827 9837 9848 9858 9869 9879 +9890 9900 9911 9921 9932 9942 9953 9963 +9974 9981 9989 9997 10005 10013 10021 10029 +10037 10046 10055 10064 10073 10082 10091 10100 +10115 10131 10147 10163 10173 10184 10194 10205 +10215 10226 10235 10244 10253 10262 10271 10280 +10289 10299 10310 10320 10331 10341 10352 10373 +10394 10415 10430 10446 10462 10478 10487 10496 +10505 10514 10523 10532 10541 10553 10566 10578 +10591 10604 10619 10635 10651 10667 10676 10685 +10694 10703 10712 10721 10730 10742 10755 10767 +10780 10793 10803 10814 10824 10835 10845 10856 +10868 10881 10893 10906 10919 10929 10940 10950 +10961 10971 10982 10992 11003 11013 11024 11034 +11045 11060 11076 11092 11108 11123 11139 11155 +11171 11181 11192 11202 11213 11223 11234 11244 +11255 11265 11276 11286 11297 11307 11318 11329 +11339 11350 11361 11371 11381 11392 11402 11412 +11423 11433 11444 11454 11465 11475 11486 11518 +11550 11560 11571 11581 11592 11602 11613 11628 +11644 11660 11676 11697 11718 11739 11749 11760 +11770 11781 11791 11802 11817 11833 11849 11865 +11875 11886 11896 11907 11917 11928 11959 11991 +12006 12022 12038 12054 12069 12085 12101 12117 +12127 12138 12148 12159 12169 12180 12201 12222 +12243 12274 12306 12321 12337 12353 12369 12384 +12400 12416 12432 12453 12474 12495 12507 12520 +12532 12545 12558 12579 12600 12621 12642 12663 +12684 12705 12726 12747 12759 12772 12784 12797 +12810 12822 12835 12847 12860 12873 12894 12915 +12936 12948 12961 12973 12986 12999 13020 13041 +13062 13093 13125 13140 13156 13172 13188 13209 +13230 13252 13283 13314 13378 13399 13420 13441 +13451 13462 13472 13483 13493 13504 13519 13535 +13551 13567 13630 13651 13672 13693 13714 13735 +13756 13787 13819 13882 13897 13913 13929 13945 +13966 13987 14008 14029 14050 14071 14092 14113 +14134 14197 14212 14228 14244 14260 14281 14302 +14323 14354 14386 14417 14449 14480 14512 14533 +14554 14575 14606 14638 14669 14701 14764 14795 +14827 14842 14858 14874 14890 14953 14984 15016 +15048 15080 15111 15143 15174 15205 15226 15247 +15269 15284 15300 15316 15332 15395 15458 15479 +15500 15521 15584 15615 15647 15668 15689 15710 +15741 15773 15836 15899 15930 15962 15993 16025 +16056 16088 16151 16214 16245 16277 16340 16403 +16466 16529 16592 16718 16781 16844 16907 16971 +17034 17065 17097 17160 17223 17254 17286 17412 +17443 17475 17538 17601 17664 17727 17916 18042 +18105 18136 18168 18294 18325 18357 18546 18577 +18609 18735 18830 18925 18956 18988 19114 19177 +19240 19303 19366 19429 19555 19618 19681 19807 +19870 19933 20185 20374 20564 20753 20942 21068 +21509 21761 22139 22455 22644 23148 23400 23652 +24030 24913 25985 26174 26993 28128 28947 29136 +30082 30712 32767 32767 32767 32767 32767 32767 +32767 32767 32767 32767 32767 32767 32767 32767 diff --git a/netem/maketable b/netem/maketable new file mode 100755 index 0000000000000000000000000000000000000000..9fbd1ca3b278f09dee0c6c5ee890c6846fe85754 GIT binary patch literal 10667 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≫!DcEJFNIkn0FO3#N$qeUn_E6HKjW7HBru&CK$EgW^x|1 z;-2z%TPyBma^AAy_);kMy`}^wzO>1C#snkZTQF}J^M@5*itp45;kPn5-mUVtm46pm z@!OdkpH_VSd!`lt6Ej<mJ8?f?q2Oe+8@n+$%U}EF(~NOb!&cbZg{J*V_CfjgsnnqH zQ$b%|ZS_F=lZplEvxVa~)@K{XZ)YQ7o&n2481D+K_QwS-$5|(E2l`LD&Ce$UK4QZ^ z&wpc|QOkTb|DWdh*0J*MBk&ZAb=AwfbqeR@%UCaOEZ5&cfc%`o*9~(R1G7+lz6e|_ zF<ksZq4XZdYi>9fnFC&n(-eC@q;n9nog>1|tu|bKS8oLFb$p=P+wlS5wT|1__iXL5 zf!nEJ^znuQKidF(*k)gTpMQevEbx8)DS;mo_T@bM18(QW`Q)h0em~Ehv7gy+`Q4sQ zE9(}RXC`d&hlQQr+VEonpRwUz7Pt<4p?a&s;zRk;S1k(sYzXvEfqOBI<a){z@d^le z`+3y;B)>qrgn;{SdSah9ws1Q)&YzvazWto+@z1fJ<MPrw#%Mo7;gAiN_Y|JB;qpB1 zc^kgbFvHFOU#K1~0<UE^&XfNp<WJc8;oElmXb%b>+i-c`!G(2uq58ZNxOu!RG0d<4 z@LJQlBlpSc$j$=wzY(}k=$gq+l3$=5bDty6FFKg6O0ak)m(Ar<sX!dJ8P<1f*&2<6 zw{_qKMKl_+a2wZ`lllWq48Q67dy=_iBG3%cL^`^=C%r4y6XhoZ(O7;Du0qiHK_cJV zyH}cS+Z>v=ESU$l1~;`uB}duF_`Fqq?7*$|#-2#>lV<L)?fQkHn6F;Y!L@PDjq4$y zM>YpHh1ZLFAXY)+4u}=Qg%2EvL}QsuY;QE#m!Lvs)6uS2U!o@ohUXm5=DC=-Dq^)@ z+#<OF>+Z8cQGN(RcTudkaZ|;L88=P%O&Du}ammGs88=m|n59(wu8g@T+>>Y&XEq`u zex)VKJv7w>o$Lq!qx8Iz=ynWU!|Bf?$&N{$F3?!ri1Y6bR*bIcSP^;W#)c;O$x?Jf zWJ_x>65aCPgWK9Vq8-83NE<pWwG>_MvFgyR94lfelu;4<N{>w{yafftm7r)oo8(=< SRP+TeAz4c=YuIu%>3;yC3L$&| literal 0 HcmV?d00001 diff --git a/netem/maketable.c b/netem/maketable.c new file mode 100644 index 0000000..ce09176 --- /dev/null +++ b/netem/maketable.c @@ -0,0 +1,232 @@ +/* + * Experimental data distribution table generator + * Taken from the uncopyrighted NISTnet code. + * + * Rread in a series of "random" data values, either + * experimentally or generated from some probability distribution. + * From this, create the inverse distribution table used to approximate + * the distribution. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <malloc.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + + +double * +readdoubles(FILE *fp, int *number) +{ + struct stat info; + double *x; + int limit; + int n=0, i; + + fstat(fileno(fp), &info); + if (info.st_size > 0) { + limit = 2*info.st_size/sizeof(double); /* @@ approximate */ + } else { + limit = 10000; + } + + x = calloc(limit, sizeof(double)); + if (!x) { + perror("double alloc"); + exit(3); + } + + for (i=0; i<limit; ++i){ + fscanf(fp, "%lf", &x[i]); + if (feof(fp)) + break; + ++n; + } + *number = n; + return x; +} + +void +arraystats(double *x, int limit, double *mu, double *sigma, double *rho) +{ + int n=0, i; + double sumsquare=0.0, sum=0.0, top=0.0; + double sigma2=0.0; + + for (i=0; i<limit; ++i){ + sumsquare += x[i]*x[i]; + sum += x[i]; + ++n; + } + *mu = sum/(double)n; + *sigma = sqrt((sumsquare - (double)n*(*mu)*(*mu))/(double)(n-1)); + + for (i=1; i < n; ++i){ + top += ((double)x[i]- *mu)*((double)x[i-1]- *mu); + sigma2 += ((double)x[i-1] - *mu)*((double)x[i-1] - *mu); + + } + *rho = top/sigma2; +} + +/* Create a (normalized) distribution table from a set of observed + * values. The table is fixed to run from (as it happens) -4 to +4, + * with granularity .00002. + */ + +#define TABLESIZE 16384/4 +#define TABLEFACTOR 8192 +#ifndef MINSHORT +#define MINSHORT -32768 +#define MAXSHORT 32767 +#endif + +/* Since entries in the inverse are scaled by TABLEFACTOR, and can't be bigger + * than MAXSHORT, we don't bother looking at a larger domain than this: + */ +#define DISTTABLEDOMAIN ((MAXSHORT/TABLEFACTOR)+1) +#define DISTTABLEGRANULARITY 50000 +#define DISTTABLESIZE (DISTTABLEDOMAIN*DISTTABLEGRANULARITY*2) + +static int * +makedist(double *x, int limit, double mu, double sigma) +{ + int *table; + int i, index, first=DISTTABLESIZE, last=0; + double input; + + table = calloc(DISTTABLESIZE, sizeof(int)); + if (!table) { + perror("table alloc"); + exit(3); + } + + for (i=0; i < limit; ++i) { + /* Normalize value */ + input = (x[i]-mu)/sigma; + + index = (int)rint((input+DISTTABLEDOMAIN)*DISTTABLEGRANULARITY); + if (index < 0) index = 0; + if (index >= DISTTABLESIZE) index = DISTTABLESIZE-1; + ++table[index]; + if (index > last) + last = index +1; + if (index < first) + first = index; + } + return table; +} + +/* replace an array by its cumulative distribution */ +static void +cumulativedist(int *table, int limit, int *total) +{ + int accum=0; + + while (--limit >= 0) { + accum += *table; + *table++ = accum; + } + *total = accum; +} + +static short * +inverttable(int *table, int inversesize, int tablesize, int cumulative) +{ + int i, inverseindex, inversevalue; + short *inverse; + double findex, fvalue; + + inverse = (short *)malloc(inversesize*sizeof(short)); + for (i=0; i < inversesize; ++i) { + inverse[i] = MINSHORT; + } + for (i=0; i < tablesize; ++i) { + findex = ((double)i/(double)DISTTABLEGRANULARITY) - DISTTABLEDOMAIN; + fvalue = (double)table[i]/(double)cumulative; + inverseindex = (int)rint(fvalue*inversesize); + inversevalue = (int)rint(findex*TABLEFACTOR); + if (inversevalue <= MINSHORT) inversevalue = MINSHORT+1; + if (inversevalue > MAXSHORT) inversevalue = MAXSHORT; + inverse[inverseindex] = inversevalue; + } + return inverse; + +} + +/* Run simple linear interpolation over the table to fill in missing entries */ +static void +interpolatetable(short *table, int limit) +{ + int i, j, last, lasti = -1; + + last = MINSHORT; + for (i=0; i < limit; ++i) { + if (table[i] == MINSHORT) { + for (j=i; j < limit; ++j) + if (table[j] != MINSHORT) + break; + if (j < limit) { + table[i] = last + (i-lasti)*(table[j]-last)/(j-lasti); + } else { + table[i] = last + (i-lasti)*(MAXSHORT-last)/(limit-lasti); + } + } else { + last = table[i]; + lasti = i; + } + } +} + +static void +printtable(const short *table, int limit) +{ + int i; + + printf("# This is the distribution table for the experimental distribution.\n"); + + for (i=0 ; i < limit; ++i) { + printf("%d%c", table[i], + (i % 8) == 7 ? '\n' : ' '); + } +} + +int +main(int argc, char **argv) +{ + FILE *fp; + double *x; + double mu, sigma, rho; + int limit; + int *table; + short *inverse; + int total; + + if (argc > 1) { + if (!(fp = fopen(argv[1], "r"))) { + perror(argv[1]); + exit(1); + } + } else { + fp = stdin; + } + x = readdoubles(fp, &limit); + if (limit <= 0) { + fprintf(stderr, "Nothing much read!\n"); + exit(2); + } + arraystats(x, limit, &mu, &sigma, &rho); +#ifdef DEBUG + fprintf(stderr, "%d values, mu %10.4f, sigma %10.4f, rho %10.4f\n", + limit, mu, sigma, rho); +#endif + + table = makedist(x, limit, mu, sigma); + free((void *) x); + cumulativedist(table, DISTTABLESIZE, &total); + inverse = inverttable(table, TABLESIZE, DISTTABLESIZE, total); + interpolatetable(inverse, TABLESIZE); + printtable(inverse, TABLESIZE); + return 0; +} diff --git a/netem/normal b/netem/normal new file mode 100755 index 0000000000000000000000000000000000000000..a42860d18fcd1aa6e733e7d8957cbb4e2404845a GIT binary patch literal 8450 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 literal 0 HcmV?d00001 diff --git a/netem/normal.c b/netem/normal.c new file mode 100644 index 0000000..e6683db --- /dev/null +++ b/netem/normal.c @@ -0,0 +1,56 @@ +/* + * Normal distribution table generator + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <limits.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +static double +normal(double x, double mu, double sigma) +{ + return .5 + .5*erf((x-mu)/(sqrt(2.0)*sigma)); +} + +int +main(int argc, char **argv) +{ + double x, *table; + int i, n; + + table = calloc(sizeof(double), TABLESIZE+1); + if (!table) { + fprintf(stderr, "Not enough memory\n"); + return 1; + } + + + for (x = -10.0; x < 10.05; x += .00005) { + i = (int)rint(TABLESIZE*normal(x, 0.0, 1.0)); + table[i] = x; + } + + + printf("# This is the distribution table for the normal distribution.\n"); + for (i = n = 0; i < TABLESIZE; i += 4) { + int value = (int) rint(table[i]*TABLEFACTOR); + if (value < SHRT_MIN) value = SHRT_MIN; + if (value > SHRT_MAX) value = SHRT_MAX; + + printf(" %d", value); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + free(table); + return 0; +} diff --git a/netem/normal.dist b/netem/normal.dist new file mode 100644 index 0000000..8cfe768 --- /dev/null +++ b/netem/normal.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the normal distribution. + -32768 -28307 -26871 -25967 -25298 -24765 -24320 -23937 + -23600 -23298 -23025 -22776 -22546 -22333 -22133 -21946 + -21770 -21604 -21445 -21295 -21151 -21013 -20882 -20755 + -20633 -20516 -20403 -20293 -20187 -20084 -19984 -19887 + -19793 -19702 -19612 -19526 -19441 -19358 -19277 -19198 + -19121 -19045 -18971 -18899 -18828 -18758 -18690 -18623 + -18557 -18492 -18429 -18366 -18305 -18245 -18185 -18127 + -18070 -18013 -17957 -17902 -17848 -17794 -17741 -17690 + -17638 -17588 -17538 -17489 -17440 -17392 -17345 -17298 + -17252 -17206 -17160 -17116 -17071 -17028 -16984 -16942 + -16899 -16857 -16816 -16775 -16735 -16694 -16654 -16615 + -16576 -16538 -16499 -16461 -16424 -16386 -16350 -16313 + -16277 -16241 -16205 -16170 -16135 -16100 -16066 -16031 + -15998 -15964 -15931 -15897 -15865 -15832 -15800 -15768 + -15736 -15704 -15673 -15642 -15611 -15580 -15550 -15519 + -15489 -15460 -15430 -15401 -15371 -15342 -15313 -15285 + -15256 -15228 -15200 -15172 -15144 -15116 -15089 -15062 + -15035 -15008 -14981 -14954 -14928 -14902 -14875 -14850 + -14823 -14798 -14772 -14747 -14722 -14696 -14671 -14647 + -14622 -14597 -14573 -14549 -14524 -14500 -14476 -14453 + -14429 -14405 -14382 -14359 -14335 -14312 -14289 -14266 + -14243 -14221 -14198 -14176 -14153 -14131 -14109 -14087 + -14065 -14043 -14021 -14000 -13978 -13957 -13935 -13914 + -13893 -13872 -13851 -13830 -13809 -13788 -13768 -13747 + -13727 -13706 -13686 -13666 -13646 -13626 -13606 -13586 + -13566 -13547 -13527 -13507 -13488 -13468 -13449 -13430 + -13411 -13392 -13373 -13354 -13335 -13316 -13297 -13278 + -13260 -13242 -13223 -13204 -13186 -13168 -13150 -13131 + -13113 -13095 -13077 -13060 -13042 -13024 -13006 -12988 + -12971 -12954 -12936 -12918 -12901 -12884 -12867 -12850 + -12832 -12815 -12798 -12781 -12764 -12748 -12731 -12714 + -12697 -12681 -12664 -12648 -12631 -12615 -12598 -12582 + -12566 -12549 -12533 -12517 -12501 -12485 -12469 -12453 + -12437 -12422 -12406 -12390 -12374 -12358 -12343 -12327 + -12312 -12296 -12281 -12265 -12250 -12235 -12220 -12204 + -12189 -12174 -12159 -12144 -12129 -12114 -12099 -12084 + -12069 -12054 -12039 -12025 -12010 -11995 -11981 -11966 + -11952 -11937 -11923 -11908 -11894 -11879 -11865 -11851 + -11837 -11822 -11808 -11794 -11780 -11766 -11752 -11737 + -11724 -11710 -11696 -11682 -11668 -11654 -11640 -11627 + -11613 -11599 -11586 -11572 -11559 -11545 -11531 -11518 + -11504 -11491 -11478 -11464 -11451 -11438 -11425 -11411 + -11398 -11385 -11372 -11359 -11346 -11332 -11319 -11306 + -11293 -11280 -11268 -11255 -11242 -11229 -11216 -11203 + -11191 -11178 -11165 -11153 -11140 -11127 -11114 -11102 + -11090 -11077 -11065 -11052 -11040 -11027 -11015 -11002 + -10990 -10978 -10965 -10953 -10941 -10929 -10917 -10904 + -10892 -10880 -10868 -10856 -10844 -10832 -10820 -10808 + -10796 -10784 -10772 -10760 -10748 -10736 -10725 -10713 + -10701 -10689 -10677 -10666 -10654 -10643 -10631 -10619 + -10607 -10596 -10584 -10573 -10562 -10550 -10539 -10527 + -10516 -10504 -10493 -10481 -10470 -10459 -10447 -10436 + -10425 -10414 -10402 -10391 -10380 -10369 -10358 -10346 + -10335 -10324 -10313 -10302 -10291 -10280 -10269 -10258 + -10247 -10236 -10225 -10214 -10203 -10192 -10181 -10171 + -10160 -10149 -10138 -10127 -10117 -10106 -10095 -10085 + -10074 -10063 -10052 -10042 -10031 -10021 -10010 -10000 + -9989 -9978 -9968 -9957 -9947 -9936 -9926 -9916 + -9905 -9895 -9884 -9874 -9864 -9853 -9843 -9833 + -9822 -9812 -9802 -9791 -9781 -9771 -9761 -9751 + -9741 -9730 -9720 -9710 -9700 -9690 -9680 -9670 + -9660 -9650 -9640 -9630 -9619 -9610 -9600 -9590 + -9580 -9570 -9560 -9550 -9540 -9530 -9520 -9511 + -9501 -9491 -9481 -9472 -9462 -9452 -9442 -9432 + -9423 -9413 -9403 -9394 -9384 -9374 -9365 -9355 + -9345 -9336 -9326 -9317 -9307 -9298 -9288 -9278 + -9269 -9259 -9250 -9241 -9231 -9221 -9212 -9202 + -9193 -9184 -9175 -9165 -9156 -9146 -9137 -9128 + -9119 -9109 -9100 -9090 -9081 -9072 -9063 -9053 + -9044 -9035 -9026 -9017 -9008 -8998 -8989 -8980 + -8971 -8962 -8953 -8944 -8934 -8925 -8916 -8907 + -8898 -8889 -8880 -8871 -8862 -8853 -8844 -8835 + -8826 -8817 -8808 -8799 -8790 -8781 -8772 -8764 + -8755 -8746 -8737 -8728 -8719 -8711 -8702 -8693 + -8684 -8675 -8667 -8658 -8649 -8640 -8632 -8623 + -8614 -8605 -8597 -8588 -8579 -8570 -8562 -8553 + -8545 -8536 -8527 -8519 -8510 -8502 -8493 -8484 + -8476 -8467 -8459 -8450 -8442 -8433 -8425 -8416 + -8408 -8399 -8391 -8382 -8374 -8365 -8357 -8348 + -8340 -8332 -8323 -8315 -8306 -8298 -8290 -8281 + -8273 -8264 -8256 -8248 -8240 -8231 -8223 -8215 + -8206 -8198 -8190 -8182 -8174 -8165 -8157 -8149 + -8140 -8132 -8124 -8116 -8108 -8099 -8091 -8083 + -8075 -8067 -8059 -8051 -8042 -8034 -8027 -8018 + -8010 -8002 -7994 -7986 -7978 -7970 -7962 -7954 + -7946 -7938 -7930 -7922 -7913 -7906 -7897 -7890 + -7882 -7874 -7866 -7858 -7850 -7842 -7834 -7826 + -7818 -7810 -7802 -7795 -7787 -7779 -7771 -7763 + -7755 -7748 -7739 -7732 -7724 -7716 -7708 -7700 + -7693 -7685 -7677 -7669 -7662 -7654 -7646 -7638 + -7630 -7623 -7615 -7608 -7600 -7592 -7584 -7577 + -7569 -7561 -7553 -7546 -7538 -7530 -7523 -7515 + -7508 -7500 -7492 -7485 -7477 -7469 -7462 -7454 + -7447 -7439 -7432 -7424 -7417 -7409 -7401 -7394 + -7386 -7379 -7372 -7364 -7356 -7349 -7341 -7334 + -7327 -7319 -7311 -7304 -7297 -7289 -7281 -7274 + -7267 -7259 -7252 -7245 -7237 -7230 -7222 -7215 + -7208 -7200 -7193 -7186 -7178 -7171 -7163 -7156 + -7149 -7141 -7134 -7127 -7119 -7112 -7105 -7098 + -7090 -7083 -7075 -7068 -7061 -7054 -7046 -7039 + -7032 -7025 -7018 -7010 -7003 -6996 -6989 -6981 + -6974 -6967 -6960 -6953 -6946 -6938 -6931 -6924 + -6917 -6910 -6903 -6895 -6888 -6881 -6874 -6867 + -6860 -6853 -6845 -6838 -6831 -6824 -6817 -6810 + -6803 -6796 -6789 -6782 -6775 -6767 -6760 -6753 + -6747 -6740 -6732 -6725 -6718 -6711 -6704 -6697 + -6690 -6683 -6676 -6669 -6662 -6655 -6648 -6641 + -6634 -6627 -6620 -6613 -6607 -6600 -6593 -6586 + -6579 -6572 -6565 -6558 -6551 -6544 -6538 -6531 + -6524 -6517 -6510 -6503 -6496 -6489 -6482 -6476 + -6469 -6462 -6455 -6448 -6441 -6434 -6428 -6421 + -6414 -6407 -6400 -6394 -6387 -6380 -6373 -6366 + -6360 -6353 -6346 -6339 -6333 -6326 -6319 -6312 + -6306 -6299 -6292 -6286 -6279 -6272 -6265 -6259 + -6252 -6245 -6239 -6232 -6225 -6219 -6212 -6205 + -6198 -6192 -6185 -6178 -6172 -6165 -6158 -6152 + -6145 -6139 -6132 -6125 -6119 -6112 -6105 -6099 + -6092 -6085 -6079 -6072 -6066 -6059 -6053 -6046 + -6040 -6033 -6026 -6019 -6013 -6006 -6000 -5993 + -5987 -5980 -5974 -5967 -5961 -5954 -5948 -5941 + -5935 -5928 -5922 -5915 -5908 -5902 -5895 -5889 + -5883 -5876 -5870 -5863 -5857 -5850 -5844 -5837 + -5831 -5825 -5818 -5811 -5805 -5799 -5792 -5786 + -5779 -5773 -5766 -5760 -5754 -5747 -5741 -5734 + -5728 -5722 -5715 -5709 -5702 -5696 -5690 -5683 + -5677 -5671 -5664 -5658 -5651 -5645 -5639 -5632 + -5626 -5620 -5613 -5607 -5600 -5594 -5588 -5582 + -5575 -5569 -5563 -5556 -5550 -5544 -5537 -5531 + -5525 -5519 -5512 -5506 -5500 -5494 -5487 -5481 + -5475 -5468 -5462 -5456 -5450 -5443 -5437 -5431 + -5425 -5418 -5412 -5406 -5400 -5393 -5387 -5381 + -5375 -5369 -5362 -5356 -5350 -5344 -5337 -5331 + -5325 -5319 -5313 -5306 -5300 -5294 -5288 -5282 + -5276 -5270 -5263 -5257 -5251 -5245 -5239 -5233 + -5226 -5220 -5214 -5208 -5202 -5196 -5190 -5183 + -5177 -5171 -5165 -5159 -5153 -5147 -5140 -5135 + -5129 -5122 -5116 -5110 -5104 -5098 -5092 -5086 + -5080 -5074 -5068 -5061 -5055 -5050 -5043 -5037 + -5031 -5025 -5019 -5013 -5007 -5001 -4995 -4989 + -4983 -4977 -4971 -4965 -4959 -4953 -4947 -4941 + -4935 -4929 -4923 -4917 -4911 -4905 -4899 -4893 + -4887 -4881 -4875 -4869 -4863 -4857 -4851 -4845 + -4839 -4833 -4827 -4821 -4815 -4809 -4803 -4797 + -4791 -4785 -4779 -4773 -4767 -4762 -4755 -4750 + -4744 -4738 -4732 -4726 -4720 -4714 -4708 -4702 + -4696 -4690 -4685 -4678 -4673 -4667 -4661 -4655 + -4649 -4643 -4637 -4631 -4626 -4620 -4614 -4608 + -4602 -4596 -4590 -4585 -4579 -4573 -4567 -4561 + -4555 -4549 -4544 -4538 -4532 -4526 -4520 -4514 + -4508 -4503 -4497 -4491 -4485 -4479 -4474 -4468 + -4462 -4456 -4450 -4445 -4439 -4433 -4427 -4421 + -4415 -4410 -4404 -4398 -4392 -4386 -4381 -4375 + -4369 -4363 -4358 -4352 -4346 -4340 -4334 -4329 + -4323 -4317 -4311 -4306 -4300 -4294 -4289 -4283 + -4277 -4271 -4266 -4260 -4254 -4248 -4243 -4237 + -4231 -4225 -4220 -4214 -4208 -4202 -4197 -4191 + -4185 -4180 -4174 -4168 -4162 -4157 -4151 -4146 + -4140 -4134 -4128 -4123 -4117 -4111 -4105 -4100 + -4094 -4089 -4083 -4077 -4071 -4066 -4060 -4055 + -4049 -4043 -4037 -4032 -4026 -4021 -4015 -4009 + -4003 -3998 -3992 -3987 -3981 -3975 -3970 -3964 + -3958 -3953 -3947 -3942 -3936 -3930 -3925 -3919 + -3913 -3908 -3902 -3897 -3891 -3885 -3880 -3874 + -3869 -3863 -3857 -3852 -3846 -3840 -3835 -3829 + -3824 -3818 -3813 -3807 -3801 -3796 -3790 -3785 + -3779 -3774 -3768 -3762 -3757 -3751 -3746 -3740 + -3734 -3729 -3723 -3718 -3712 -3707 -3701 -3696 + -3690 -3684 -3679 -3673 -3668 -3662 -3657 -3651 + -3646 -3640 -3635 -3629 -3624 -3618 -3613 -3607 + -3602 -3596 -3591 -3585 -3579 -3574 -3568 -3563 + -3557 -3552 -3546 -3541 -3535 -3530 -3524 -3519 + -3514 -3508 -3502 -3497 -3491 -3486 -3480 -3475 + -3469 -3464 -3459 -3453 -3448 -3442 -3437 -3431 + -3425 -3420 -3415 -3409 -3404 -3398 -3393 -3387 + -3382 -3376 -3371 -3366 -3360 -3355 -3349 -3344 + -3338 -3333 -3328 -3322 -3317 -3311 -3305 -3300 + -3295 -3289 -3284 -3278 -3273 -3268 -3262 -3257 + -3251 -3246 -3240 -3235 -3230 -3224 -3219 -3213 + -3208 -3203 -3197 -3192 -3186 -3181 -3176 -3170 + -3165 -3159 -3154 -3149 -3143 -3138 -3132 -3127 + -3122 -3116 -3111 -3105 -3100 -3095 -3089 -3084 + -3079 -3073 -3068 -3062 -3057 -3052 -3046 -3041 + -3036 -3030 -3025 -3019 -3014 -3009 -3003 -2998 + -2993 -2987 -2982 -2977 -2971 -2966 -2961 -2955 + -2950 -2944 -2939 -2934 -2928 -2923 -2918 -2912 + -2907 -2902 -2896 -2891 -2886 -2880 -2875 -2870 + -2864 -2859 -2854 -2848 -2843 -2838 -2832 -2827 + -2822 -2816 -2811 -2806 -2800 -2795 -2790 -2784 + -2779 -2774 -2768 -2763 -2758 -2753 -2747 -2742 + -2737 -2732 -2726 -2721 -2716 -2710 -2705 -2700 + -2694 -2689 -2684 -2678 -2673 -2668 -2663 -2657 + -2652 -2647 -2642 -2636 -2631 -2626 -2620 -2615 + -2610 -2605 -2599 -2594 -2589 -2583 -2578 -2573 + -2568 -2562 -2557 -2552 -2546 -2542 -2536 -2531 + -2526 -2520 -2515 -2510 -2505 -2499 -2494 -2489 + -2483 -2478 -2473 -2468 -2463 -2457 -2452 -2447 + -2442 -2436 -2431 -2426 -2421 -2415 -2410 -2405 + -2400 -2395 -2389 -2384 -2379 -2374 -2368 -2363 + -2358 -2353 -2347 -2342 -2337 -2332 -2327 -2321 + -2316 -2311 -2306 -2300 -2295 -2290 -2285 -2279 + -2275 -2269 -2264 -2259 -2254 -2248 -2243 -2238 + -2233 -2227 -2222 -2217 -2212 -2207 -2202 -2196 + -2191 -2186 -2181 -2175 -2170 -2165 -2160 -2155 + -2150 -2144 -2139 -2134 -2129 -2124 -2118 -2113 + -2108 -2103 -2098 -2093 -2087 -2082 -2077 -2072 + -2067 -2062 -2056 -2051 -2046 -2041 -2036 -2030 + -2025 -2020 -2015 -2010 -2005 -2000 -1994 -1989 + -1984 -1979 -1974 -1969 -1963 -1958 -1953 -1948 + -1943 -1937 -1932 -1927 -1922 -1917 -1912 -1907 + -1901 -1896 -1891 -1886 -1881 -1876 -1871 -1865 + -1860 -1855 -1850 -1845 -1840 -1835 -1829 -1824 + -1819 -1814 -1809 -1804 -1799 -1794 -1788 -1783 + -1778 -1773 -1768 -1763 -1758 -1752 -1747 -1742 + -1737 -1732 -1727 -1722 -1717 -1711 -1706 -1701 + -1696 -1691 -1686 -1681 -1676 -1670 -1665 -1660 + -1655 -1650 -1645 -1640 -1635 -1629 -1624 -1619 + -1614 -1609 -1604 -1599 -1594 -1589 -1584 -1579 + -1573 -1568 -1563 -1558 -1553 -1548 -1543 -1538 + -1532 -1527 -1522 -1517 -1512 -1507 -1502 -1497 + -1492 -1486 -1482 -1477 -1471 -1466 -1461 -1456 + -1451 -1446 -1441 -1436 -1431 -1425 -1420 -1415 + -1410 -1405 -1400 -1395 -1390 -1385 -1380 -1375 + -1370 -1364 -1359 -1354 -1349 -1344 -1339 -1334 + -1329 -1324 -1319 -1314 -1309 -1303 -1298 -1294 + -1288 -1283 -1278 -1273 -1268 -1263 -1258 -1253 + -1248 -1243 -1237 -1232 -1228 -1222 -1217 -1212 + -1207 -1202 -1197 -1192 -1187 -1182 -1177 -1171 + -1167 -1162 -1156 -1151 -1146 -1141 -1136 -1131 + -1126 -1121 -1116 -1111 -1106 -1101 -1096 -1091 + -1085 -1081 -1076 -1070 -1065 -1060 -1055 -1050 + -1045 -1040 -1035 -1030 -1025 -1020 -1015 -1010 + -1005 -1000 -995 -990 -985 -979 -974 -970 + -964 -959 -954 -949 -944 -939 -934 -929 + -924 -919 -914 -909 -904 -899 -894 -889 + -884 -879 -874 -868 -863 -859 -853 -848 + -843 -838 -833 -828 -823 -818 -813 -808 + -803 -798 -793 -788 -783 -778 -773 -768 + -763 -758 -752 -748 -743 -738 -732 -727 + -723 -717 -712 -707 -702 -697 -692 -687 + -682 -677 -672 -667 -662 -657 -652 -647 + -642 -637 -632 -627 -622 -617 -612 -607 + -602 -597 -591 -587 -582 -577 -571 -566 + -562 -557 -551 -546 -541 -537 -531 -526 + -521 -516 -511 -506 -501 -496 -491 -486 + -481 -476 -471 -466 -461 -456 -451 -446 + -441 -436 -431 -426 -421 -416 -411 -406 + -401 -396 -391 -386 -381 -376 -371 -366 + -360 -356 -351 -346 -340 -335 -331 -326 + -320 -315 -310 -306 -300 -295 -290 -285 + -281 -275 -270 -265 -261 -255 -250 -245 + -240 -235 -230 -225 -220 -215 -210 -205 + -200 -195 -190 -185 -180 -175 -170 -165 + -160 -155 -150 -145 -140 -135 -130 -125 + -120 -115 -110 -105 -100 -95 -90 -85 + -80 -75 -70 -65 -60 -55 -50 -45 + -40 -35 -29 -25 -20 -15 -9 -5 + 0 5 11 16 20 25 30 36 + 41 45 50 56 61 66 70 76 + 81 86 91 96 101 106 111 116 + 121 126 131 136 141 146 151 156 + 161 166 171 176 181 186 191 196 + 201 206 211 216 221 226 231 236 + 241 246 251 256 261 266 271 276 + 281 286 291 296 301 306 311 316 + 322 326 331 336 342 347 351 356 + 362 367 372 376 382 387 392 396 + 402 407 412 417 422 427 432 437 + 442 447 452 457 462 467 472 477 + 482 487 492 497 502 507 512 517 + 522 527 532 537 542 547 552 557 + 562 567 572 578 582 587 593 598 + 603 607 613 618 623 628 633 638 + 643 648 653 658 663 668 673 678 + 683 688 693 698 703 708 713 718 + 723 728 733 739 743 748 754 759 + 763 768 774 779 784 789 794 799 + 804 809 814 819 824 829 834 839 + 844 849 854 859 864 869 874 879 + 884 890 895 899 905 910 915 920 + 925 930 935 940 945 950 955 960 + 965 970 975 980 985 990 995 1001 + 1006 1010 1016 1021 1026 1031 1036 1041 + 1046 1051 1056 1061 1066 1071 1076 1081 + 1086 1092 1096 1102 1107 1112 1117 1122 + 1127 1132 1137 1142 1147 1152 1157 1162 + 1167 1173 1178 1183 1188 1193 1198 1203 + 1208 1213 1218 1223 1228 1233 1238 1244 + 1248 1254 1259 1264 1269 1274 1279 1284 + 1289 1294 1299 1304 1309 1314 1320 1325 + 1330 1335 1340 1345 1350 1355 1360 1365 + 1371 1375 1381 1386 1391 1396 1401 1406 + 1411 1416 1421 1426 1432 1436 1442 1447 + 1452 1457 1462 1467 1472 1477 1482 1488 + 1493 1497 1503 1508 1513 1518 1523 1528 + 1534 1538 1543 1549 1554 1559 1564 1569 + 1574 1579 1584 1590 1595 1600 1605 1610 + 1615 1620 1625 1630 1636 1640 1646 1651 + 1656 1661 1666 1671 1676 1681 1687 1692 + 1697 1702 1707 1712 1717 1722 1728 1733 + 1738 1743 1748 1753 1758 1764 1769 1774 + 1779 1784 1789 1794 1799 1805 1810 1815 + 1820 1825 1831 1835 1841 1846 1851 1856 + 1861 1866 1871 1877 1882 1887 1892 1897 + 1902 1908 1913 1918 1923 1928 1933 1939 + 1944 1949 1954 1959 1964 1969 1975 1980 + 1985 1990 1995 2000 2005 2011 2016 2021 + 2026 2031 2037 2042 2047 2052 2057 2062 + 2068 2073 2078 2083 2088 2093 2099 2104 + 2109 2114 2119 2125 2130 2135 2140 2145 + 2150 2156 2161 2166 2171 2177 2182 2187 + 2192 2197 2202 2208 2213 2218 2223 2229 + 2234 2239 2244 2249 2254 2260 2265 2270 + 2275 2281 2286 2291 2296 2302 2306 2312 + 2317 2322 2327 2333 2338 2343 2348 2354 + 2359 2364 2369 2374 2380 2385 2390 2395 + 2401 2406 2411 2416 2422 2427 2432 2437 + 2442 2448 2453 2458 2463 2469 2474 2479 + 2485 2490 2495 2500 2506 2511 2516 2521 + 2526 2532 2537 2542 2548 2553 2558 2563 + 2569 2574 2579 2585 2589 2595 2600 2605 + 2611 2616 2621 2627 2632 2637 2642 2648 + 2653 2658 2664 2669 2674 2680 2685 2690 + 2695 2700 2706 2711 2716 2722 2727 2732 + 2738 2743 2748 2754 2759 2764 2769 2775 + 2780 2785 2791 2796 2801 2807 2812 2817 + 2823 2828 2833 2839 2844 2849 2855 2860 + 2865 2870 2876 2881 2886 2892 2897 2902 + 2908 2913 2918 2924 2929 2935 2940 2945 + 2951 2956 2961 2967 2972 2977 2983 2988 + 2993 2999 3004 3010 3015 3020 3026 3031 + 3036 3042 3047 3052 3058 3063 3069 3074 + 3079 3085 3090 3095 3101 3106 3112 3117 + 3122 3128 3133 3139 3144 3149 3155 3160 + 3166 3171 3176 3182 3187 3193 3198 3203 + 3209 3214 3220 3225 3231 3236 3242 3247 + 3252 3258 3263 3269 3274 3279 3285 3290 + 3296 3301 3307 3312 3317 3323 3328 3334 + 3339 3345 3350 3355 3361 3367 3372 3378 + 3383 3388 3394 3399 3405 3410 3416 3421 + 3427 3432 3437 3443 3448 3454 3459 3465 + 3471 3476 3481 3487 3492 3498 3503 3509 + 3514 3520 3525 3531 3536 3542 3548 3553 + 3558 3564 3569 3575 3580 3586 3591 3597 + 3602 3608 3613 3619 3625 3630 3636 3641 + 3647 3652 3658 3663 3669 3675 3680 3686 + 3691 3697 3702 3708 3713 3719 3724 3730 + 3736 3741 3747 3752 3758 3763 3769 3774 + 3780 3786 3791 3797 3802 3808 3813 3819 + 3825 3830 3836 3842 3847 3853 3858 3864 + 3869 3875 3881 3886 3892 3898 3903 3909 + 3915 3920 3926 3931 3937 3942 3948 3954 + 3960 3965 3971 3976 3982 3987 3993 3999 + 4005 4010 4016 4021 4027 4033 4039 4044 + 4050 4055 4061 4067 4073 4078 4084 4089 + 4095 4101 4107 4112 4118 4123 4129 4135 + 4141 4146 4152 4158 4164 4169 4175 4181 + 4187 4192 4198 4203 4209 4215 4221 4226 + 4232 4238 4243 4249 4255 4261 4266 4272 + 4278 4284 4289 4295 4301 4307 4313 4318 + 4324 4330 4336 4341 4347 4353 4359 4364 + 4370 4376 4382 4388 4393 4399 4405 4411 + 4417 4422 4428 4434 4440 4445 4452 4457 + 4463 4469 4474 4481 4486 4492 4498 4504 + 4510 4515 4521 4527 4533 4539 4545 4551 + 4556 4562 4568 4574 4580 4585 4592 4597 + 4603 4609 4615 4621 4627 4633 4638 4644 + 4650 4656 4662 4668 4674 4680 4686 4692 + 4697 4703 4709 4715 4721 4727 4733 4739 + 4745 4751 4757 4762 4769 4774 4780 4786 + 4792 4798 4804 4810 4816 4822 4828 4834 + 4840 4846 4852 4858 4864 4870 4876 4882 + 4888 4894 4900 4906 4912 4918 4924 4930 + 4936 4942 4948 4954 4960 4966 4972 4978 + 4984 4990 4996 5002 5008 5014 5020 5026 + 5032 5038 5045 5050 5057 5063 5069 5075 + 5081 5087 5093 5099 5105 5111 5118 5123 + 5129 5136 5142 5148 5154 5160 5166 5172 + 5179 5185 5191 5197 5203 5209 5215 5221 + 5227 5233 5240 5246 5252 5258 5265 5271 + 5277 5283 5289 5295 5301 5308 5314 5320 + 5326 5333 5339 5345 5351 5357 5363 5369 + 5376 5382 5388 5394 5401 5407 5413 5419 + 5426 5432 5438 5444 5451 5457 5463 5469 + 5476 5482 5488 5494 5501 5507 5513 5520 + 5526 5532 5539 5545 5551 5557 5564 5570 + 5576 5583 5589 5596 5602 5608 5614 5621 + 5627 5634 5640 5646 5652 5659 5665 5672 + 5678 5684 5691 5697 5704 5710 5716 5723 + 5729 5736 5742 5748 5755 5761 5768 5774 + 5780 5787 5793 5800 5806 5813 5819 5826 + 5832 5838 5845 5852 5858 5864 5871 5877 + 5884 5890 5897 5903 5910 5916 5923 5929 + 5936 5942 5949 5956 5962 5968 5975 5981 + 5988 5994 6001 6008 6014 6021 6027 6034 + 6041 6047 6054 6060 6067 6074 6080 6087 + 6093 6100 6107 6113 6120 6126 6133 6140 + 6146 6153 6160 6167 6173 6180 6186 6193 + 6200 6206 6213 6220 6226 6233 6240 6246 + 6253 6260 6266 6273 6280 6287 6294 6300 + 6307 6314 6321 6327 6334 6341 6348 6354 + 6361 6368 6375 6382 6388 6395 6402 6409 + 6416 6422 6429 6436 6443 6450 6457 6463 + 6470 6477 6484 6491 6497 6504 6511 6518 + 6525 6532 6539 6546 6553 6559 6566 6573 + 6580 6587 6594 6601 6608 6615 6622 6629 + 6636 6643 6650 6657 6664 6671 6678 6685 + 6692 6699 6706 6713 6719 6727 6734 6741 + 6748 6755 6762 6769 6776 6783 6790 6797 + 6804 6811 6818 6826 6833 6840 6847 6854 + 6861 6868 6875 6883 6889 6897 6904 6911 + 6918 6925 6932 6939 6947 6954 6961 6969 + 6975 6983 6990 6997 7005 7012 7019 7026 + 7033 7041 7048 7055 7062 7070 7077 7084 + 7091 7099 7106 7114 7121 7128 7135 7143 + 7150 7157 7165 7172 7179 7187 7194 7202 + 7209 7216 7224 7231 7238 7246 7253 7261 + 7268 7276 7283 7290 7298 7306 7313 7320 + 7328 7336 7343 7350 7358 7365 7373 7381 + 7388 7395 7403 7410 7418 7426 7433 7441 + 7448 7456 7463 7471 7479 7486 7494 7501 + 7509 7517 7524 7532 7540 7547 7555 7563 + 7571 7578 7586 7594 7601 7609 7617 7624 + 7632 7640 7648 7655 7663 7671 7679 7687 + 7694 7702 7710 7718 7725 7733 7741 7749 + 7757 7765 7773 7780 7788 7796 7804 7812 + 7820 7828 7836 7843 7852 7859 7868 7875 + 7883 7891 7899 7907 7915 7923 7931 7939 + 7947 7955 7963 7971 7979 7988 7995 8004 + 8012 8020 8028 8036 8044 8052 8061 8069 + 8076 8085 8093 8101 8109 8117 8126 8134 + 8142 8150 8158 8167 8175 8183 8192 8200 + 8208 8217 8225 8233 8241 8250 8258 8266 + 8275 8283 8292 8300 8308 8317 8325 8333 + 8342 8350 8359 8367 8376 8384 8392 8401 + 8409 8418 8426 8435 8443 8452 8461 8469 + 8477 8486 8495 8503 8512 8520 8529 8538 + 8546 8555 8564 8573 8581 8590 8598 8607 + 8616 8625 8633 8642 8651 8659 8668 8677 + 8686 8695 8704 8712 8721 8730 8739 8748 + 8756 8765 8774 8783 8792 8801 8810 8819 + 8828 8837 8846 8855 8864 8873 8882 8891 + 8900 8909 8918 8927 8936 8945 8954 8964 + 8973 8982 8991 9000 9009 9019 9028 9037 + 9046 9055 9064 9074 9083 9092 9102 9111 + 9120 9130 9139 9148 9157 9167 9176 9186 + 9195 9205 9214 9223 9233 9242 9252 9261 + 9271 9280 9290 9300 9309 9318 9328 9338 + 9347 9357 9367 9376 9386 9395 9405 9415 + 9424 9434 9444 9454 9464 9473 9483 9493 + 9503 9513 9522 9532 9542 9552 9562 9572 + 9582 9592 9602 9612 9622 9632 9642 9652 + 9662 9672 9682 9692 9702 9712 9722 9733 + 9743 9753 9763 9773 9783 9794 9804 9814 + 9825 9835 9845 9855 9866 9876 9887 9897 + 9907 9918 9928 9939 9949 9960 9970 9981 + 9991 10002 10012 10023 10034 10044 10055 10066 + 10076 10087 10097 10108 10119 10130 10140 10152 + 10162 10173 10184 10195 10206 10217 10227 10238 + 10249 10260 10271 10282 10293 10304 10315 10326 + 10337 10349 10360 10371 10382 10394 10405 10416 + 10427 10438 10450 10461 10472 10484 10495 10507 + 10518 10530 10541 10553 10564 10575 10587 10598 + 10610 10622 10633 10645 10657 10668 10680 10692 + 10704 10715 10727 10739 10751 10763 10775 10786 + 10798 10811 10822 10834 10847 10858 10870 10883 + 10895 10907 10919 10931 10944 10956 10968 10981 + 10993 11005 11017 11030 11042 11055 11067 11080 + 11092 11105 11117 11130 11142 11155 11168 11180 + 11193 11206 11219 11232 11245 11257 11270 11283 + 11296 11309 11322 11335 11348 11361 11375 11388 + 11401 11414 11427 11441 11454 11467 11481 11494 + 11508 11521 11534 11548 11561 11575 11589 11602 + 11616 11630 11644 11657 11671 11685 11699 11713 + 11727 11741 11755 11769 11783 11797 11811 11826 + 11839 11854 11868 11882 11897 11911 11926 11940 + 11955 11969 11984 11998 12013 12028 12043 12057 + 12072 12087 12102 12117 12132 12147 12162 12177 + 12193 12208 12223 12238 12254 12269 12284 12299 + 12315 12331 12346 12362 12378 12393 12409 12425 + 12441 12457 12473 12489 12505 12521 12537 12553 + 12569 12586 12602 12619 12635 12651 12668 12684 + 12701 12718 12734 12751 12768 12785 12802 12819 + 12836 12853 12870 12888 12905 12922 12940 12957 + 12975 12993 13010 13028 13046 13064 13081 13099 + 13117 13135 13154 13172 13190 13209 13227 13246 + 13264 13283 13301 13320 13339 13358 13377 13396 + 13415 13434 13454 13473 13492 13512 13532 13551 + 13571 13591 13611 13631 13651 13671 13691 13711 + 13732 13752 13773 13793 13814 13835 13856 13877 + 13898 13919 13940 13962 13983 14005 14026 14048 + 14070 14092 14114 14136 14159 14181 14203 14226 + 14249 14272 14294 14318 14341 14364 14387 14411 + 14434 14458 14482 14506 14530 14554 14578 14603 + 14628 14653 14677 14703 14728 14753 14778 14804 + 14830 14855 14882 14908 14934 14961 14987 15014 + 15041 15068 15095 15123 15151 15179 15206 15235 + 15263 15291 15320 15349 15378 15408 15437 15466 + 15496 15527 15557 15587 15618 15649 15680 15712 + 15743 15775 15808 15840 15872 15906 15939 15972 + 16006 16040 16074 16108 16143 16178 16214 16249 + 16285 16322 16358 16395 16433 16470 16508 16547 + 16586 16624 16664 16704 16744 16785 16826 16867 + 16910 16952 16995 17038 17082 17126 17171 17217 + 17263 17309 17356 17403 17452 17501 17550 17600 + 17651 17702 17754 17807 17861 17915 17970 18026 + 18083 18141 18200 18259 18320 18382 18444 18508 + 18573 18639 18706 18775 18845 18917 18989 19064 + 19140 19217 19297 19378 19461 19547 19634 19724 + 19816 19911 20009 20109 20213 20319 20430 20544 + 20663 20786 20914 21047 21186 21331 21484 21644 + 21813 21991 22181 22384 22601 22836 23091 23370 + 23679 24027 24424 24888 25450 26164 27159 28858 diff --git a/netem/pareto b/netem/pareto new file mode 100755 index 0000000000000000000000000000000000000000..8030cb81bb1b342afd095692dd9e5f26f3c366e9 GIT binary patch literal 7772 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 literal 0 HcmV?d00001 diff --git a/netem/pareto.c b/netem/pareto.c new file mode 100644 index 0000000..8aa647b --- /dev/null +++ b/netem/pareto.c @@ -0,0 +1,41 @@ +/* + * Pareto distribution table generator + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <limits.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +static const double a=3.0; +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +int +main(int argc, char **argv) +{ + int i, n; + double dvalue; + + printf("# This is the distribution table for the pareto distribution.\n"); + + for (i = 65536, n = 0; i > 0; i -= 16) { + dvalue = (double)i/(double)65536; + dvalue = 1.0/pow(dvalue, 1.0/a); + dvalue -= 1.5; + dvalue *= (4.0/3.0)*(double)TABLEFACTOR; + if (dvalue > 32767) + dvalue = 32767; + + printf(" %d", (int)rint(dvalue)); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + + return 0; +} diff --git a/netem/pareto.dist b/netem/pareto.dist new file mode 100644 index 0000000..295e6fb --- /dev/null +++ b/netem/pareto.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the pareto distribution. + -5461 -5460 -5460 -5459 -5458 -5457 -5456 -5455 + -5454 -5453 -5452 -5452 -5451 -5450 -5449 -5448 + -5447 -5446 -5445 -5444 -5443 -5443 -5442 -5441 + -5440 -5439 -5438 -5437 -5436 -5435 -5435 -5434 + -5433 -5432 -5431 -5430 -5429 -5428 -5427 -5426 + -5426 -5425 -5424 -5423 -5422 -5421 -5420 -5419 + -5418 -5417 -5417 -5416 -5415 -5414 -5413 -5412 + -5411 -5410 -5409 -5408 -5407 -5407 -5406 -5405 + -5404 -5403 -5402 -5401 -5400 -5399 -5398 -5397 + -5397 -5396 -5395 -5394 -5393 -5392 -5391 -5390 + -5389 -5388 -5387 -5387 -5386 -5385 -5384 -5383 + -5382 -5381 -5380 -5379 -5378 -5377 -5376 -5376 + -5375 -5374 -5373 -5372 -5371 -5370 -5369 -5368 + -5367 -5366 -5365 -5365 -5364 -5363 -5362 -5361 + -5360 -5359 -5358 -5357 -5356 -5355 -5354 -5353 + -5353 -5352 -5351 -5350 -5349 -5348 -5347 -5346 + -5345 -5344 -5343 -5342 -5341 -5340 -5340 -5339 + -5338 -5337 -5336 -5335 -5334 -5333 -5332 -5331 + -5330 -5329 -5328 -5327 -5327 -5326 -5325 -5324 + -5323 -5322 -5321 -5320 -5319 -5318 -5317 -5316 + -5315 -5314 -5313 -5312 -5312 -5311 -5310 -5309 + -5308 -5307 -5306 -5305 -5304 -5303 -5302 -5301 + -5300 -5299 -5298 -5297 -5296 -5296 -5295 -5294 + -5293 -5292 -5291 -5290 -5289 -5288 -5287 -5286 + -5285 -5284 -5283 -5282 -5281 -5280 -5279 -5278 + -5278 -5277 -5276 -5275 -5274 -5273 -5272 -5271 + -5270 -5269 -5268 -5267 -5266 -5265 -5264 -5263 + -5262 -5261 -5260 -5259 -5258 -5258 -5257 -5256 + -5255 -5254 -5253 -5252 -5251 -5250 -5249 -5248 + -5247 -5246 -5245 -5244 -5243 -5242 -5241 -5240 + -5239 -5238 -5237 -5236 -5235 -5234 -5233 -5233 + -5232 -5231 -5230 -5229 -5228 -5227 -5226 -5225 + -5224 -5223 -5222 -5221 -5220 -5219 -5218 -5217 + -5216 -5215 -5214 -5213 -5212 -5211 -5210 -5209 + -5208 -5207 -5206 -5205 -5204 -5203 -5202 -5201 + -5200 -5199 -5199 -5198 -5197 -5196 -5195 -5194 + -5193 -5192 -5191 -5190 -5189 -5188 -5187 -5186 + -5185 -5184 -5183 -5182 -5181 -5180 -5179 -5178 + -5177 -5176 -5175 -5174 -5173 -5172 -5171 -5170 + -5169 -5168 -5167 -5166 -5165 -5164 -5163 -5162 + -5161 -5160 -5159 -5158 -5157 -5156 -5155 -5154 + -5153 -5152 -5151 -5150 -5149 -5148 -5147 -5146 + -5145 -5144 -5143 -5142 -5141 -5140 -5139 -5138 + -5137 -5136 -5135 -5134 -5133 -5132 -5131 -5130 + -5129 -5128 -5127 -5126 -5125 -5124 -5123 -5122 + -5121 -5120 -5119 -5118 -5117 -5116 -5115 -5114 + -5113 -5112 -5111 -5110 -5109 -5108 -5107 -5106 + -5105 -5104 -5103 -5102 -5101 -5100 -5099 -5098 + -5097 -5096 -5095 -5094 -5093 -5092 -5091 -5090 + -5089 -5088 -5087 -5086 -5085 -5084 -5083 -5082 + -5081 -5080 -5079 -5078 -5077 -5076 -5075 -5074 + -5073 -5072 -5071 -5069 -5068 -5067 -5066 -5065 + -5064 -5063 -5062 -5061 -5060 -5059 -5058 -5057 + -5056 -5055 -5054 -5053 -5052 -5051 -5050 -5049 + -5048 -5047 -5046 -5045 -5044 -5043 -5042 -5041 + -5040 -5039 -5038 -5037 -5036 -5034 -5033 -5032 + -5031 -5030 -5029 -5028 -5027 -5026 -5025 -5024 + -5023 -5022 -5021 -5020 -5019 -5018 -5017 -5016 + -5015 -5014 -5013 -5012 -5011 -5009 -5008 -5007 + -5006 -5005 -5004 -5003 -5002 -5001 -5000 -4999 + -4998 -4997 -4996 -4995 -4994 -4993 -4992 -4991 + -4990 -4989 -4987 -4986 -4985 -4984 -4983 -4982 + -4981 -4980 -4979 -4978 -4977 -4976 -4975 -4974 + -4973 -4972 -4971 -4969 -4968 -4967 -4966 -4965 + -4964 -4963 -4962 -4961 -4960 -4959 -4958 -4957 + -4956 -4955 -4954 -4952 -4951 -4950 -4949 -4948 + -4947 -4946 -4945 -4944 -4943 -4942 -4941 -4940 + -4939 -4938 -4936 -4935 -4934 -4933 -4932 -4931 + -4930 -4929 -4928 -4927 -4926 -4925 -4924 -4922 + -4921 -4920 -4919 -4918 -4917 -4916 -4915 -4914 + -4913 -4912 -4911 -4909 -4908 -4907 -4906 -4905 + -4904 -4903 -4902 -4901 -4900 -4899 -4898 -4896 + -4895 -4894 -4893 -4892 -4891 -4890 -4889 -4888 + -4887 -4886 -4884 -4883 -4882 -4881 -4880 -4879 + -4878 -4877 -4876 -4875 -4874 -4872 -4871 -4870 + -4869 -4868 -4867 -4866 -4865 -4864 -4863 -4861 + -4860 -4859 -4858 -4857 -4856 -4855 -4854 -4853 + -4852 -4850 -4849 -4848 -4847 -4846 -4845 -4844 + -4843 -4842 -4840 -4839 -4838 -4837 -4836 -4835 + -4834 -4833 -4832 -4830 -4829 -4828 -4827 -4826 + -4825 -4824 -4823 -4822 -4820 -4819 -4818 -4817 + -4816 -4815 -4814 -4813 -4811 -4810 -4809 -4808 + -4807 -4806 -4805 -4804 -4803 -4801 -4800 -4799 + -4798 -4797 -4796 -4795 -4794 -4792 -4791 -4790 + -4789 -4788 -4787 -4786 -4784 -4783 -4782 -4781 + -4780 -4779 -4778 -4777 -4775 -4774 -4773 -4772 + -4771 -4770 -4769 -4767 -4766 -4765 -4764 -4763 + -4762 -4761 -4760 -4758 -4757 -4756 -4755 -4754 + -4753 -4752 -4750 -4749 -4748 -4747 -4746 -4745 + -4743 -4742 -4741 -4740 -4739 -4738 -4737 -4735 + -4734 -4733 -4732 -4731 -4730 -4729 -4727 -4726 + -4725 -4724 -4723 -4722 -4720 -4719 -4718 -4717 + -4716 -4715 -4714 -4712 -4711 -4710 -4709 -4708 + -4707 -4705 -4704 -4703 -4702 -4701 -4700 -4698 + -4697 -4696 -4695 -4694 -4693 -4691 -4690 -4689 + -4688 -4687 -4686 -4684 -4683 -4682 -4681 -4680 + -4679 -4677 -4676 -4675 -4674 -4673 -4672 -4670 + -4669 -4668 -4667 -4666 -4664 -4663 -4662 -4661 + -4660 -4659 -4657 -4656 -4655 -4654 -4653 -4651 + -4650 -4649 -4648 -4647 -4646 -4644 -4643 -4642 + -4641 -4640 -4638 -4637 -4636 -4635 -4634 -4632 + -4631 -4630 -4629 -4628 -4627 -4625 -4624 -4623 + -4622 -4621 -4619 -4618 -4617 -4616 -4615 -4613 + -4612 -4611 -4610 -4609 -4607 -4606 -4605 -4604 + -4603 -4601 -4600 -4599 -4598 -4597 -4595 -4594 + -4593 -4592 -4590 -4589 -4588 -4587 -4586 -4584 + -4583 -4582 -4581 -4580 -4578 -4577 -4576 -4575 + -4574 -4572 -4571 -4570 -4569 -4567 -4566 -4565 + -4564 -4563 -4561 -4560 -4559 -4558 -4556 -4555 + -4554 -4553 -4552 -4550 -4549 -4548 -4547 -4545 + -4544 -4543 -4542 -4541 -4539 -4538 -4537 -4536 + -4534 -4533 -4532 -4531 -4529 -4528 -4527 -4526 + -4525 -4523 -4522 -4521 -4520 -4518 -4517 -4516 + -4515 -4513 -4512 -4511 -4510 -4508 -4507 -4506 + -4505 -4503 -4502 -4501 -4500 -4498 -4497 -4496 + -4495 -4493 -4492 -4491 -4490 -4488 -4487 -4486 + -4485 -4483 -4482 -4481 -4480 -4478 -4477 -4476 + -4475 -4473 -4472 -4471 -4470 -4468 -4467 -4466 + -4465 -4463 -4462 -4461 -4460 -4458 -4457 -4456 + -4455 -4453 -4452 -4451 -4449 -4448 -4447 -4446 + -4444 -4443 -4442 -4441 -4439 -4438 -4437 -4435 + -4434 -4433 -4432 -4430 -4429 -4428 -4427 -4425 + -4424 -4423 -4421 -4420 -4419 -4418 -4416 -4415 + -4414 -4412 -4411 -4410 -4409 -4407 -4406 -4405 + -4404 -4402 -4401 -4400 -4398 -4397 -4396 -4394 + -4393 -4392 -4391 -4389 -4388 -4387 -4385 -4384 + -4383 -4382 -4380 -4379 -4378 -4376 -4375 -4374 + -4372 -4371 -4370 -4369 -4367 -4366 -4365 -4363 + -4362 -4361 -4359 -4358 -4357 -4356 -4354 -4353 + -4352 -4350 -4349 -4348 -4346 -4345 -4344 -4342 + -4341 -4340 -4338 -4337 -4336 -4335 -4333 -4332 + -4331 -4329 -4328 -4327 -4325 -4324 -4323 -4321 + -4320 -4319 -4317 -4316 -4315 -4313 -4312 -4311 + -4309 -4308 -4307 -4305 -4304 -4303 -4301 -4300 + -4299 -4297 -4296 -4295 -4293 -4292 -4291 -4289 + -4288 -4287 -4285 -4284 -4283 -4281 -4280 -4279 + -4277 -4276 -4275 -4273 -4272 -4271 -4269 -4268 + -4267 -4265 -4264 -4263 -4261 -4260 -4259 -4257 + -4256 -4254 -4253 -4252 -4250 -4249 -4248 -4246 + -4245 -4244 -4242 -4241 -4240 -4238 -4237 -4236 + -4234 -4233 -4231 -4230 -4229 -4227 -4226 -4225 + -4223 -4222 -4221 -4219 -4218 -4216 -4215 -4214 + -4212 -4211 -4210 -4208 -4207 -4205 -4204 -4203 + -4201 -4200 -4199 -4197 -4196 -4194 -4193 -4192 + -4190 -4189 -4188 -4186 -4185 -4183 -4182 -4181 + -4179 -4178 -4176 -4175 -4174 -4172 -4171 -4170 + -4168 -4167 -4165 -4164 -4163 -4161 -4160 -4158 + -4157 -4156 -4154 -4153 -4151 -4150 -4149 -4147 + -4146 -4144 -4143 -4142 -4140 -4139 -4137 -4136 + -4135 -4133 -4132 -4130 -4129 -4128 -4126 -4125 + -4123 -4122 -4120 -4119 -4118 -4116 -4115 -4113 + -4112 -4111 -4109 -4108 -4106 -4105 -4103 -4102 + -4101 -4099 -4098 -4096 -4095 -4094 -4092 -4091 + -4089 -4088 -4086 -4085 -4084 -4082 -4081 -4079 + -4078 -4076 -4075 -4073 -4072 -4071 -4069 -4068 + -4066 -4065 -4063 -4062 -4061 -4059 -4058 -4056 + -4055 -4053 -4052 -4050 -4049 -4048 -4046 -4045 + -4043 -4042 -4040 -4039 -4037 -4036 -4035 -4033 + -4032 -4030 -4029 -4027 -4026 -4024 -4023 -4021 + -4020 -4018 -4017 -4016 -4014 -4013 -4011 -4010 + -4008 -4007 -4005 -4004 -4002 -4001 -3999 -3998 + -3997 -3995 -3994 -3992 -3991 -3989 -3988 -3986 + -3985 -3983 -3982 -3980 -3979 -3977 -3976 -3974 + -3973 -3971 -3970 -3968 -3967 -3965 -3964 -3963 + -3961 -3960 -3958 -3957 -3955 -3954 -3952 -3951 + -3949 -3948 -3946 -3945 -3943 -3942 -3940 -3939 + -3937 -3936 -3934 -3933 -3931 -3930 -3928 -3927 + -3925 -3924 -3922 -3921 -3919 -3918 -3916 -3915 + -3913 -3912 -3910 -3909 -3907 -3905 -3904 -3902 + -3901 -3899 -3898 -3896 -3895 -3893 -3892 -3890 + -3889 -3887 -3886 -3884 -3883 -3881 -3880 -3878 + -3877 -3875 -3874 -3872 -3870 -3869 -3867 -3866 + -3864 -3863 -3861 -3860 -3858 -3857 -3855 -3854 + -3852 -3851 -3849 -3847 -3846 -3844 -3843 -3841 + -3840 -3838 -3837 -3835 -3834 -3832 -3830 -3829 + -3827 -3826 -3824 -3823 -3821 -3820 -3818 -3816 + -3815 -3813 -3812 -3810 -3809 -3807 -3805 -3804 + -3802 -3801 -3799 -3798 -3796 -3795 -3793 -3791 + -3790 -3788 -3787 -3785 -3784 -3782 -3780 -3779 + -3777 -3776 -3774 -3772 -3771 -3769 -3768 -3766 + -3765 -3763 -3761 -3760 -3758 -3757 -3755 -3753 + -3752 -3750 -3749 -3747 -3746 -3744 -3742 -3741 + -3739 -3738 -3736 -3734 -3733 -3731 -3730 -3728 + -3726 -3725 -3723 -3722 -3720 -3718 -3717 -3715 + -3713 -3712 -3710 -3709 -3707 -3705 -3704 -3702 + -3701 -3699 -3697 -3696 -3694 -3692 -3691 -3689 + -3688 -3686 -3684 -3683 -3681 -3680 -3678 -3676 + -3675 -3673 -3671 -3670 -3668 -3666 -3665 -3663 + -3662 -3660 -3658 -3657 -3655 -3653 -3652 -3650 + -3648 -3647 -3645 -3644 -3642 -3640 -3639 -3637 + -3635 -3634 -3632 -3630 -3629 -3627 -3625 -3624 + -3622 -3620 -3619 -3617 -3615 -3614 -3612 -3610 + -3609 -3607 -3605 -3604 -3602 -3600 -3599 -3597 + -3595 -3594 -3592 -3590 -3589 -3587 -3585 -3584 + -3582 -3580 -3579 -3577 -3575 -3574 -3572 -3570 + -3569 -3567 -3565 -3564 -3562 -3560 -3558 -3557 + -3555 -3553 -3552 -3550 -3548 -3547 -3545 -3543 + -3542 -3540 -3538 -3536 -3535 -3533 -3531 -3530 + -3528 -3526 -3524 -3523 -3521 -3519 -3518 -3516 + -3514 -3513 -3511 -3509 -3507 -3506 -3504 -3502 + -3501 -3499 -3497 -3495 -3494 -3492 -3490 -3488 + -3487 -3485 -3483 -3482 -3480 -3478 -3476 -3475 + -3473 -3471 -3469 -3468 -3466 -3464 -3462 -3461 + -3459 -3457 -3455 -3454 -3452 -3450 -3448 -3447 + -3445 -3443 -3441 -3440 -3438 -3436 -3434 -3433 + -3431 -3429 -3427 -3426 -3424 -3422 -3420 -3419 + -3417 -3415 -3413 -3412 -3410 -3408 -3406 -3404 + -3403 -3401 -3399 -3397 -3396 -3394 -3392 -3390 + -3388 -3387 -3385 -3383 -3381 -3380 -3378 -3376 + -3374 -3372 -3371 -3369 -3367 -3365 -3363 -3362 + -3360 -3358 -3356 -3354 -3353 -3351 -3349 -3347 + -3345 -3344 -3342 -3340 -3338 -3336 -3335 -3333 + -3331 -3329 -3327 -3326 -3324 -3322 -3320 -3318 + -3316 -3315 -3313 -3311 -3309 -3307 -3305 -3304 + -3302 -3300 -3298 -3296 -3295 -3293 -3291 -3289 + -3287 -3285 -3283 -3282 -3280 -3278 -3276 -3274 + -3272 -3271 -3269 -3267 -3265 -3263 -3261 -3259 + -3258 -3256 -3254 -3252 -3250 -3248 -3246 -3245 + -3243 -3241 -3239 -3237 -3235 -3233 -3232 -3230 + -3228 -3226 -3224 -3222 -3220 -3218 -3217 -3215 + -3213 -3211 -3209 -3207 -3205 -3203 -3202 -3200 + -3198 -3196 -3194 -3192 -3190 -3188 -3186 -3185 + -3183 -3181 -3179 -3177 -3175 -3173 -3171 -3169 + -3167 -3166 -3164 -3162 -3160 -3158 -3156 -3154 + -3152 -3150 -3148 -3146 -3144 -3143 -3141 -3139 + -3137 -3135 -3133 -3131 -3129 -3127 -3125 -3123 + -3121 -3119 -3117 -3116 -3114 -3112 -3110 -3108 + -3106 -3104 -3102 -3100 -3098 -3096 -3094 -3092 + -3090 -3088 -3086 -3084 -3082 -3081 -3079 -3077 + -3075 -3073 -3071 -3069 -3067 -3065 -3063 -3061 + -3059 -3057 -3055 -3053 -3051 -3049 -3047 -3045 + -3043 -3041 -3039 -3037 -3035 -3033 -3031 -3029 + -3027 -3025 -3023 -3021 -3019 -3017 -3015 -3013 + -3011 -3009 -3007 -3005 -3003 -3001 -2999 -2997 + -2995 -2993 -2991 -2989 -2987 -2985 -2983 -2981 + -2979 -2977 -2975 -2973 -2971 -2969 -2967 -2965 + -2963 -2961 -2959 -2957 -2955 -2953 -2951 -2949 + -2947 -2945 -2943 -2941 -2939 -2937 -2935 -2933 + -2931 -2928 -2926 -2924 -2922 -2920 -2918 -2916 + -2914 -2912 -2910 -2908 -2906 -2904 -2902 -2900 + -2898 -2896 -2893 -2891 -2889 -2887 -2885 -2883 + -2881 -2879 -2877 -2875 -2873 -2871 -2869 -2866 + -2864 -2862 -2860 -2858 -2856 -2854 -2852 -2850 + -2848 -2846 -2843 -2841 -2839 -2837 -2835 -2833 + -2831 -2829 -2827 -2825 -2822 -2820 -2818 -2816 + -2814 -2812 -2810 -2808 -2805 -2803 -2801 -2799 + -2797 -2795 -2793 -2791 -2788 -2786 -2784 -2782 + -2780 -2778 -2776 -2773 -2771 -2769 -2767 -2765 + -2763 -2761 -2758 -2756 -2754 -2752 -2750 -2748 + -2745 -2743 -2741 -2739 -2737 -2735 -2733 -2730 + -2728 -2726 -2724 -2722 -2719 -2717 -2715 -2713 + -2711 -2709 -2706 -2704 -2702 -2700 -2698 -2695 + -2693 -2691 -2689 -2687 -2684 -2682 -2680 -2678 + -2676 -2673 -2671 -2669 -2667 -2665 -2662 -2660 + -2658 -2656 -2654 -2651 -2649 -2647 -2645 -2642 + -2640 -2638 -2636 -2633 -2631 -2629 -2627 -2625 + -2622 -2620 -2618 -2616 -2613 -2611 -2609 -2607 + -2604 -2602 -2600 -2598 -2595 -2593 -2591 -2589 + -2586 -2584 -2582 -2579 -2577 -2575 -2573 -2570 + -2568 -2566 -2564 -2561 -2559 -2557 -2554 -2552 + -2550 -2548 -2545 -2543 -2541 -2538 -2536 -2534 + -2532 -2529 -2527 -2525 -2522 -2520 -2518 -2515 + -2513 -2511 -2508 -2506 -2504 -2501 -2499 -2497 + -2495 -2492 -2490 -2488 -2485 -2483 -2481 -2478 + -2476 -2474 -2471 -2469 -2467 -2464 -2462 -2459 + -2457 -2455 -2452 -2450 -2448 -2445 -2443 -2441 + -2438 -2436 -2434 -2431 -2429 -2426 -2424 -2422 + -2419 -2417 -2415 -2412 -2410 -2407 -2405 -2403 + -2400 -2398 -2396 -2393 -2391 -2388 -2386 -2384 + -2381 -2379 -2376 -2374 -2372 -2369 -2367 -2364 + -2362 -2359 -2357 -2355 -2352 -2350 -2347 -2345 + -2343 -2340 -2338 -2335 -2333 -2330 -2328 -2325 + -2323 -2321 -2318 -2316 -2313 -2311 -2308 -2306 + -2303 -2301 -2299 -2296 -2294 -2291 -2289 -2286 + -2284 -2281 -2279 -2276 -2274 -2271 -2269 -2266 + -2264 -2261 -2259 -2257 -2254 -2252 -2249 -2247 + -2244 -2242 -2239 -2237 -2234 -2232 -2229 -2227 + -2224 -2222 -2219 -2216 -2214 -2211 -2209 -2206 + -2204 -2201 -2199 -2196 -2194 -2191 -2189 -2186 + -2184 -2181 -2179 -2176 -2173 -2171 -2168 -2166 + -2163 -2161 -2158 -2156 -2153 -2150 -2148 -2145 + -2143 -2140 -2138 -2135 -2132 -2130 -2127 -2125 + -2122 -2120 -2117 -2114 -2112 -2109 -2107 -2104 + -2101 -2099 -2096 -2094 -2091 -2088 -2086 -2083 + -2081 -2078 -2075 -2073 -2070 -2067 -2065 -2062 + -2060 -2057 -2054 -2052 -2049 -2046 -2044 -2041 + -2038 -2036 -2033 -2031 -2028 -2025 -2023 -2020 + -2017 -2015 -2012 -2009 -2007 -2004 -2001 -1999 + -1996 -1993 -1991 -1988 -1985 -1983 -1980 -1977 + -1974 -1972 -1969 -1966 -1964 -1961 -1958 -1956 + -1953 -1950 -1947 -1945 -1942 -1939 -1937 -1934 + -1931 -1928 -1926 -1923 -1920 -1917 -1915 -1912 + -1909 -1907 -1904 -1901 -1898 -1896 -1893 -1890 + -1887 -1884 -1882 -1879 -1876 -1873 -1871 -1868 + -1865 -1862 -1860 -1857 -1854 -1851 -1848 -1846 + -1843 -1840 -1837 -1834 -1832 -1829 -1826 -1823 + -1820 -1818 -1815 -1812 -1809 -1806 -1804 -1801 + -1798 -1795 -1792 -1789 -1787 -1784 -1781 -1778 + -1775 -1772 -1770 -1767 -1764 -1761 -1758 -1755 + -1752 -1750 -1747 -1744 -1741 -1738 -1735 -1732 + -1729 -1727 -1724 -1721 -1718 -1715 -1712 -1709 + -1706 -1703 -1701 -1698 -1695 -1692 -1689 -1686 + -1683 -1680 -1677 -1674 -1671 -1668 -1666 -1663 + -1660 -1657 -1654 -1651 -1648 -1645 -1642 -1639 + -1636 -1633 -1630 -1627 -1624 -1621 -1618 -1615 + -1612 -1609 -1606 -1603 -1600 -1597 -1594 -1591 + -1589 -1586 -1583 -1580 -1577 -1574 -1571 -1567 + -1564 -1561 -1558 -1555 -1552 -1549 -1546 -1543 + -1540 -1537 -1534 -1531 -1528 -1525 -1522 -1519 + -1516 -1513 -1510 -1507 -1504 -1501 -1498 -1495 + -1491 -1488 -1485 -1482 -1479 -1476 -1473 -1470 + -1467 -1464 -1461 -1458 -1454 -1451 -1448 -1445 + -1442 -1439 -1436 -1433 -1430 -1426 -1423 -1420 + -1417 -1414 -1411 -1408 -1404 -1401 -1398 -1395 + -1392 -1389 -1386 -1382 -1379 -1376 -1373 -1370 + -1367 -1363 -1360 -1357 -1354 -1351 -1347 -1344 + -1341 -1338 -1335 -1331 -1328 -1325 -1322 -1319 + -1315 -1312 -1309 -1306 -1302 -1299 -1296 -1293 + -1290 -1286 -1283 -1280 -1277 -1273 -1270 -1267 + -1263 -1260 -1257 -1254 -1250 -1247 -1244 -1241 + -1237 -1234 -1231 -1227 -1224 -1221 -1218 -1214 + -1211 -1208 -1204 -1201 -1198 -1194 -1191 -1188 + -1184 -1181 -1178 -1174 -1171 -1168 -1164 -1161 + -1158 -1154 -1151 -1147 -1144 -1141 -1137 -1134 + -1131 -1127 -1124 -1120 -1117 -1114 -1110 -1107 + -1103 -1100 -1097 -1093 -1090 -1086 -1083 -1080 + -1076 -1073 -1069 -1066 -1062 -1059 -1056 -1052 + -1049 -1045 -1042 -1038 -1035 -1031 -1028 -1024 + -1021 -1017 -1014 -1010 -1007 -1003 -1000 -996 + -993 -989 -986 -982 -979 -975 -972 -968 + -965 -961 -958 -954 -951 -947 -944 -940 + -936 -933 -929 -926 -922 -919 -915 -911 + -908 -904 -901 -897 -894 -890 -886 -883 + -879 -876 -872 -868 -865 -861 -857 -854 + -850 -847 -843 -839 -836 -832 -828 -825 + -821 -817 -814 -810 -806 -803 -799 -795 + -792 -788 -784 -780 -777 -773 -769 -766 + -762 -758 -754 -751 -747 -743 -740 -736 + -732 -728 -725 -721 -717 -713 -709 -706 + -702 -698 -694 -691 -687 -683 -679 -675 + -672 -668 -664 -660 -656 -653 -649 -645 + -641 -637 -633 -630 -626 -622 -618 -614 + -610 -606 -602 -599 -595 -591 -587 -583 + -579 -575 -571 -567 -564 -560 -556 -552 + -548 -544 -540 -536 -532 -528 -524 -520 + -516 -512 -508 -504 -500 -496 -493 -489 + -485 -481 -477 -473 -469 -465 -461 -456 + -452 -448 -444 -440 -436 -432 -428 -424 + -420 -416 -412 -408 -404 -400 -396 -392 + -388 -383 -379 -375 -371 -367 -363 -359 + -355 -351 -346 -342 -338 -334 -330 -326 + -322 -317 -313 -309 -305 -301 -297 -292 + -288 -284 -280 -276 -271 -267 -263 -259 + -255 -250 -246 -242 -238 -233 -229 -225 + -221 -216 -212 -208 -204 -199 -195 -191 + -186 -182 -178 -173 -169 -165 -160 -156 + -152 -147 -143 -139 -134 -130 -126 -121 + -117 -113 -108 -104 -99 -95 -91 -86 + -82 -77 -73 -69 -64 -60 -55 -51 + -46 -42 -37 -33 -29 -24 -20 -15 + -11 -6 -2 3 7 12 16 21 + 25 30 34 39 44 48 53 57 + 62 66 71 76 80 85 89 94 + 99 103 108 112 117 122 126 131 + 136 140 145 150 154 159 164 168 + 173 178 182 187 192 196 201 206 + 211 215 220 225 230 234 239 244 + 249 253 258 263 268 273 277 282 + 287 292 297 302 306 311 316 321 + 326 331 336 341 345 350 355 360 + 365 370 375 380 385 390 395 400 + 405 409 414 419 424 429 434 439 + 444 449 454 459 464 470 475 480 + 485 490 495 500 505 510 515 520 + 525 530 536 541 546 551 556 561 + 566 572 577 582 587 592 597 603 + 608 613 618 623 629 634 639 644 + 650 655 660 665 671 676 681 687 + 692 697 703 708 713 719 724 729 + 735 740 745 751 756 761 767 772 + 778 783 789 794 799 805 810 816 + 821 827 832 838 843 849 854 860 + 865 871 876 882 887 893 899 904 + 910 915 921 927 932 938 943 949 + 955 960 966 972 977 983 989 994 + 1000 1006 1011 1017 1023 1029 1034 1040 + 1046 1052 1057 1063 1069 1075 1081 1086 + 1092 1098 1104 1110 1116 1121 1127 1133 + 1139 1145 1151 1157 1163 1169 1175 1181 + 1186 1192 1198 1204 1210 1216 1222 1228 + 1234 1240 1246 1252 1258 1265 1271 1277 + 1283 1289 1295 1301 1307 1313 1319 1326 + 1332 1338 1344 1350 1356 1363 1369 1375 + 1381 1387 1394 1400 1406 1412 1419 1425 + 1431 1438 1444 1450 1456 1463 1469 1475 + 1482 1488 1495 1501 1507 1514 1520 1527 + 1533 1539 1546 1552 1559 1565 1572 1578 + 1585 1591 1598 1604 1611 1617 1624 1631 + 1637 1644 1650 1657 1664 1670 1677 1684 + 1690 1697 1704 1710 1717 1724 1730 1737 + 1744 1751 1757 1764 1771 1778 1784 1791 + 1798 1805 1812 1819 1825 1832 1839 1846 + 1853 1860 1867 1874 1881 1888 1895 1902 + 1909 1916 1923 1930 1937 1944 1951 1958 + 1965 1972 1979 1986 1993 2000 2008 2015 + 2022 2029 2036 2043 2051 2058 2065 2072 + 2080 2087 2094 2101 2109 2116 2123 2131 + 2138 2145 2153 2160 2168 2175 2182 2190 + 2197 2205 2212 2220 2227 2235 2242 2250 + 2257 2265 2272 2280 2287 2295 2303 2310 + 2318 2326 2333 2341 2349 2356 2364 2372 + 2379 2387 2395 2403 2410 2418 2426 2434 + 2442 2450 2457 2465 2473 2481 2489 2497 + 2505 2513 2521 2529 2537 2545 2553 2561 + 2569 2577 2585 2593 2601 2609 2618 2626 + 2634 2642 2650 2658 2667 2675 2683 2691 + 2700 2708 2716 2725 2733 2741 2750 2758 + 2766 2775 2783 2792 2800 2809 2817 2826 + 2834 2843 2851 2860 2868 2877 2885 2894 + 2903 2911 2920 2929 2937 2946 2955 2964 + 2972 2981 2990 2999 3008 3016 3025 3034 + 3043 3052 3061 3070 3079 3088 3097 3106 + 3115 3124 3133 3142 3151 3160 3169 3178 + 3187 3197 3206 3215 3224 3233 3243 3252 + 3261 3271 3280 3289 3299 3308 3317 3327 + 3336 3346 3355 3365 3374 3384 3393 3403 + 3412 3422 3432 3441 3451 3461 3470 3480 + 3490 3499 3509 3519 3529 3539 3549 3558 + 3568 3578 3588 3598 3608 3618 3628 3638 + 3648 3658 3668 3678 3688 3699 3709 3719 + 3729 3739 3750 3760 3770 3781 3791 3801 + 3812 3822 3832 3843 3853 3864 3874 3885 + 3895 3906 3917 3927 3938 3948 3959 3970 + 3981 3991 4002 4013 4024 4035 4045 4056 + 4067 4078 4089 4100 4111 4122 4133 4144 + 4155 4167 4178 4189 4200 4211 4223 4234 + 4245 4256 4268 4279 4291 4302 4313 4325 + 4336 4348 4359 4371 4383 4394 4406 4418 + 4429 4441 4453 4465 4476 4488 4500 4512 + 4524 4536 4548 4560 4572 4584 4596 4608 + 4620 4632 4645 4657 4669 4681 4694 4706 + 4718 4731 4743 4756 4768 4781 4793 4806 + 4818 4831 4844 4856 4869 4882 4895 4908 + 4920 4933 4946 4959 4972 4985 4998 5011 + 5024 5037 5051 5064 5077 5090 5104 5117 + 5130 5144 5157 5171 5184 5198 5211 5225 + 5238 5252 5266 5280 5293 5307 5321 5335 + 5349 5363 5377 5391 5405 5419 5433 5447 + 5461 5476 5490 5504 5519 5533 5547 5562 + 5576 5591 5605 5620 5635 5649 5664 5679 + 5694 5709 5724 5738 5753 5768 5783 5799 + 5814 5829 5844 5859 5875 5890 5905 5921 + 5936 5952 5967 5983 5999 6014 6030 6046 + 6062 6078 6094 6110 6126 6142 6158 6174 + 6190 6206 6223 6239 6255 6272 6288 6305 + 6321 6338 6355 6371 6388 6405 6422 6439 + 6456 6473 6490 6507 6524 6541 6559 6576 + 6593 6611 6628 6646 6663 6681 6699 6716 + 6734 6752 6770 6788 6806 6824 6842 6860 + 6879 6897 6915 6934 6952 6971 6989 7008 + 7027 7046 7065 7083 7102 7121 7141 7160 + 7179 7198 7218 7237 7256 7276 7296 7315 + 7335 7355 7375 7395 7415 7435 7455 7475 + 7495 7516 7536 7556 7577 7598 7618 7639 + 7660 7681 7702 7723 7744 7765 7786 7808 + 7829 7851 7872 7894 7916 7938 7959 7981 + 8003 8026 8048 8070 8092 8115 8137 8160 + 8183 8206 8228 8251 8274 8298 8321 8344 + 8367 8391 8415 8438 8462 8486 8510 8534 + 8558 8582 8606 8631 8655 8680 8705 8729 + 8754 8779 8804 8830 8855 8880 8906 8931 + 8957 8983 9009 9035 9061 9087 9113 9140 + 9166 9193 9220 9247 9274 9301 9328 9356 + 9383 9411 9438 9466 9494 9522 9550 9579 + 9607 9636 9664 9693 9722 9751 9780 9810 + 9839 9869 9898 9928 9958 9988 10019 10049 + 10080 10110 10141 10172 10203 10235 10266 10298 + 10329 10361 10393 10426 10458 10490 10523 10556 + 10589 10622 10655 10689 10722 10756 10790 10824 + 10859 10893 10928 10963 10998 11033 11068 11104 + 11139 11175 11211 11248 11284 11321 11358 11395 + 11432 11470 11507 11545 11583 11622 11660 11699 + 11738 11777 11816 11856 11896 11936 11976 12017 + 12058 12098 12140 12181 12223 12265 12307 12349 + 12392 12435 12478 12522 12566 12609 12654 12698 + 12743 12788 12834 12879 12925 12971 13018 13065 + 13112 13159 13207 13255 13303 13352 13401 13450 + 13500 13550 13600 13651 13702 13753 13805 13857 + 13909 13962 14015 14069 14123 14177 14232 14287 + 14342 14398 14454 14511 14568 14626 14684 14742 + 14801 14860 14920 14980 15041 15102 15164 15226 + 15288 15351 15415 15479 15544 15609 15675 15741 + 15808 15875 15943 16011 16080 16150 16220 16291 + 16363 16435 16508 16581 16655 16730 16805 16881 + 16958 17036 17114 17193 17273 17353 17435 17517 + 17600 17683 17768 17853 17939 18027 18115 18203 + 18293 18384 18476 18569 18662 18757 18853 18950 + 19047 19146 19246 19348 19450 19554 19658 19764 + 19872 19980 20090 20201 20314 20428 20543 20660 + 20778 20898 21020 21143 21267 21394 21522 21652 + 21783 21917 22052 22189 22329 22470 22613 22759 + 22907 23056 23209 23363 23521 23680 23842 24007 + 24175 24345 24519 24695 24874 25057 25243 25432 + 25625 25821 26021 26225 26433 26645 26861 27081 + 27307 27537 27771 28011 28257 28508 28764 29027 + 29295 29570 29852 30141 30438 30742 31054 31374 + 31704 32042 32391 32750 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 + 32767 32767 32767 32767 32767 32767 32767 32767 diff --git a/netem/paretonormal b/netem/paretonormal new file mode 100755 index 0000000000000000000000000000000000000000..925d18ff92c4ae25343f1a67a47484780693d36d GIT binary patch literal 8891 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 NXWTox{W$KK{{^j#7JUE! literal 0 HcmV?d00001 diff --git a/netem/paretonormal.c b/netem/paretonormal.c new file mode 100644 index 0000000..c793df6 --- /dev/null +++ b/netem/paretonormal.c @@ -0,0 +1,90 @@ +/* + * Paretoormal distribution table generator + * + * This distribution is simply .25*normal + .75*pareto; a combination + * which seems to match experimentally observed distributions reasonably + * well, but is computationally easy to handle. + * The entries represent a scaled inverse of the cumulative distribution + * function. + * + * Taken from the uncopyrighted NISTnet code. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <limits.h> +#include <malloc.h> + +#include <linux/types.h> +#include <linux/pkt_sched.h> + +#define TABLESIZE 16384 +#define TABLEFACTOR NETEM_DIST_SCALE + +static double +normal(double x, double mu, double sigma) +{ + return .5 + .5*erf((x-mu)/(sqrt(2.0)*sigma)); +} + + +static const double a=3.0; + +static int +paretovalue(int i) +{ + double dvalue; + + i = 65536-4*i; + dvalue = (double)i/(double)65536; + dvalue = 1.0/pow(dvalue, 1.0/a); + dvalue -= 1.5; + dvalue *= (4.0/3.0)*(double)TABLEFACTOR; + if (dvalue > 32767) + dvalue = 32767; + return (int)rint(dvalue); +} + +int +main(int argc, char **argv) +{ + double x; + double *table; + int i,n; + + table = calloc(TABLESIZE+1, sizeof(double)); + if (!table) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + + for (x = -10.0; x < 10.05; x += .00005) { + i = (int)rint(TABLESIZE*normal(x, 0.0, 1.0)); + table[i] = x; + } + printf( +"# This is the distribution table for the paretonormal distribution.\n" + ); + + for (i = n = 0; i < TABLESIZE; i += 4) { + int normvalue, parvalue, value; + + normvalue = (int) rint(table[i]*TABLEFACTOR); + parvalue = paretovalue(i); + + value = (normvalue+3*parvalue)/4; + if (value < SHRT_MIN) value = SHRT_MIN; + if (value > SHRT_MAX) value = SHRT_MAX; + + printf(" %d", value); + if (++n == 8) { + putchar('\n'); + n = 0; + } + } + free(table); + + return 0; +} diff --git a/netem/paretonormal.dist b/netem/paretonormal.dist new file mode 100644 index 0000000..61be1b8 --- /dev/null +++ b/netem/paretonormal.dist @@ -0,0 +1,513 @@ +# This is the distribution table for the paretonormal distribution. + -12305 -11171 -10812 -10586 -10418 -10284 -10172 -10075 + -9990 -9914 -9845 -9783 -9724 -9670 -9620 -9572 + -9527 -9485 -9445 -9406 -9370 -9335 -9302 -9269 + -9238 -9208 -9179 -9151 -9123 -9097 -9072 -9047 + -9023 -8999 -8976 -8954 -8932 -8910 -8889 -8869 + -8849 -8830 -8810 -8792 -8773 -8755 -8737 -8720 + -8702 -8685 -8670 -8653 -8637 -8621 -8606 -8590 + -8575 -8560 -8546 -8531 -8517 -8503 -8489 -8476 + -8462 -8449 -8436 -8423 -8410 -8397 -8384 -8372 + -8360 -8348 -8336 -8324 -8312 -8301 -8289 -8278 + -8266 -8255 -8244 -8234 -8223 -8212 -8201 -8191 + -8180 -8170 -8159 -8149 -8139 -8129 -8119 -8110 + -8100 -8090 -8081 -8071 -8062 -8052 -8043 -8033 + -8024 -8015 -8006 -7998 -7989 -7980 -7971 -7962 + -7954 -7945 -7936 -7928 -7919 -7911 -7903 -7894 + -7887 -7879 -7870 -7862 -7854 -7846 -7838 -7830 + -7822 -7815 -7807 -7799 -7791 -7784 -7777 -7769 + -7762 -7754 -7747 -7739 -7732 -7725 -7717 -7710 + -7703 -7696 -7689 -7682 -7675 -7668 -7661 -7654 + -7647 -7640 -7634 -7627 -7620 -7613 -7606 -7600 + -7593 -7586 -7580 -7573 -7567 -7561 -7554 -7548 + -7541 -7535 -7529 -7522 -7516 -7510 -7503 -7497 + -7491 -7485 -7478 -7472 -7466 -7461 -7455 -7449 + -7443 -7437 -7431 -7425 -7419 -7413 -7407 -7401 + -7395 -7389 -7383 -7378 -7372 -7366 -7360 -7355 + -7350 -7344 -7338 -7333 -7327 -7321 -7316 -7310 + -7305 -7299 -7294 -7288 -7283 -7277 -7272 -7266 + -7261 -7256 -7250 -7245 -7240 -7235 -7230 -7224 + -7219 -7214 -7209 -7204 -7198 -7193 -7188 -7183 + -7178 -7173 -7167 -7162 -7157 -7152 -7147 -7142 + -7137 -7132 -7127 -7122 -7117 -7112 -7107 -7103 + -7098 -7093 -7088 -7083 -7078 -7074 -7069 -7064 + -7059 -7054 -7049 -7045 -7040 -7035 -7030 -7026 + -7021 -7016 -7012 -7007 -7002 -6997 -6993 -6988 + -6984 -6979 -6974 -6970 -6965 -6961 -6956 -6951 + -6947 -6942 -6939 -6934 -6930 -6925 -6921 -6916 + -6912 -6907 -6903 -6898 -6894 -6889 -6885 -6881 + -6876 -6872 -6868 -6863 -6859 -6854 -6850 -6846 + -6842 -6837 -6833 -6829 -6824 -6820 -6816 -6811 + -6807 -6803 -6799 -6795 -6790 -6786 -6782 -6778 + -6774 -6769 -6765 -6761 -6757 -6753 -6749 -6745 + -6740 -6736 -6732 -6728 -6724 -6720 -6716 -6712 + -6708 -6704 -6700 -6696 -6692 -6688 -6684 -6680 + -6676 -6672 -6668 -6664 -6660 -6656 -6652 -6648 + -6644 -6640 -6636 -6632 -6628 -6624 -6620 -6617 + -6613 -6609 -6605 -6601 -6597 -6593 -6590 -6586 + -6582 -6578 -6574 -6570 -6567 -6563 -6559 -6555 + -6551 -6548 -6544 -6540 -6536 -6533 -6529 -6525 + -6521 -6518 -6514 -6510 -6506 -6503 -6499 -6495 + -6492 -6488 -6484 -6481 -6477 -6473 -6470 -6466 + -6462 -6459 -6455 -6451 -6448 -6444 -6441 -6437 + -6433 -6430 -6426 -6422 -6418 -6415 -6411 -6407 + -6404 -6400 -6397 -6393 -6390 -6386 -6383 -6379 + -6375 -6372 -6368 -6365 -6361 -6358 -6354 -6351 + -6347 -6344 -6340 -6337 -6333 -6330 -6326 -6323 + -6320 -6316 -6313 -6309 -6306 -6302 -6298 -6295 + -6291 -6288 -6284 -6281 -6278 -6274 -6271 -6268 + -6264 -6261 -6257 -6254 -6251 -6247 -6244 -6241 + -6237 -6234 -6230 -6227 -6224 -6220 -6216 -6213 + -6210 -6206 -6203 -6200 -6196 -6193 -6190 -6187 + -6183 -6180 -6177 -6173 -6170 -6167 -6164 -6160 + -6157 -6154 -6150 -6147 -6143 -6140 -6137 -6134 + -6130 -6127 -6124 -6121 -6117 -6114 -6111 -6108 + -6105 -6101 -6098 -6094 -6091 -6088 -6085 -6081 + -6078 -6075 -6072 -6069 -6066 -6062 -6059 -6056 + -6053 -6050 -6047 -6043 -6040 -6037 -6033 -6030 + -6027 -6024 -6021 -6018 -6015 -6011 -6008 -6005 + -6002 -5999 -5995 -5992 -5989 -5986 -5983 -5980 + -5977 -5974 -5971 -5967 -5964 -5961 -5958 -5954 + -5951 -5948 -5945 -5942 -5939 -5936 -5933 -5930 + -5927 -5924 -5921 -5917 -5914 -5911 -5908 -5905 + -5902 -5899 -5896 -5893 -5890 -5887 -5884 -5880 + -5877 -5874 -5871 -5868 -5865 -5862 -5859 -5857 + -5854 -5851 -5847 -5844 -5841 -5838 -5835 -5832 + -5829 -5826 -5823 -5820 -5817 -5814 -5811 -5808 + -5805 -5802 -5799 -5796 -5793 -5790 -5787 -5784 + -5781 -5778 -5775 -5772 -5769 -5766 -5763 -5760 + -5758 -5754 -5751 -5748 -5745 -5742 -5740 -5737 + -5734 -5731 -5727 -5724 -5722 -5719 -5716 -5713 + -5710 -5707 -5704 -5701 -5698 -5695 -5692 -5689 + -5687 -5684 -5681 -5678 -5675 -5672 -5669 -5666 + -5663 -5660 -5658 -5655 -5651 -5648 -5646 -5643 + -5640 -5637 -5634 -5632 -5629 -5625 -5622 -5620 + -5617 -5614 -5611 -5609 -5606 -5602 -5600 -5597 + -5594 -5591 -5588 -5586 -5582 -5579 -5577 -5574 + -5571 -5568 -5566 -5563 -5559 -5557 -5554 -5551 + -5548 -5546 -5543 -5539 -5537 -5534 -5531 -5528 + -5526 -5523 -5520 -5517 -5514 -5511 -5509 -5506 + -5503 -5501 -5497 -5494 -5492 -5489 -5486 -5483 + -5480 -5477 -5475 -5472 -5469 -5467 -5464 -5460 + -5458 -5455 -5452 -5450 -5447 -5444 -5441 -5438 + -5436 -5433 -5430 -5428 -5424 -5421 -5419 -5416 + -5414 -5411 -5408 -5405 -5402 -5399 -5397 -5394 + -5392 -5388 -5386 -5383 -5380 -5378 -5375 -5372 + -5369 -5366 -5364 -5361 -5358 -5355 -5352 -5350 + -5347 -5345 -5342 -5339 -5336 -5333 -5331 -5328 + -5326 -5322 -5320 -5317 -5314 -5312 -5309 -5306 + -5303 -5301 -5298 -5296 -5292 -5290 -5287 -5284 + -5282 -5279 -5276 -5273 -5271 -5268 -5266 -5262 + -5260 -5257 -5254 -5252 -5249 -5246 -5243 -5241 + -5238 -5236 -5233 -5230 -5227 -5225 -5222 -5219 + -5216 -5214 -5211 -5209 -5206 -5203 -5200 -5198 + -5195 -5193 -5190 -5187 -5184 -5182 -5179 -5176 + -5174 -5171 -5168 -5166 -5163 -5160 -5158 -5155 + -5153 -5149 -5147 -5144 -5142 -5139 -5136 -5133 + -5131 -5129 -5125 -5123 -5120 -5118 -5115 -5112 + -5109 -5107 -5104 -5102 -5099 -5096 -5094 -5091 + -5089 -5085 -5083 -5080 -5078 -5075 -5072 -5070 + -5067 -5065 -5062 -5059 -5057 -5054 -5051 -5049 + -5046 -5044 -5041 -5038 -5035 -5033 -5030 -5027 + -5025 -5022 -5020 -5017 -5014 -5012 -5009 -5007 + -5004 -5001 -4999 -4996 -4993 -4991 -4988 -4986 + -4983 -4980 -4978 -4975 -4973 -4970 -4967 -4965 + -4962 -4959 -4957 -4954 -4952 -4949 -4946 -4944 + -4941 -4938 -4936 -4933 -4931 -4928 -4925 -4923 + -4920 -4917 -4915 -4912 -4910 -4907 -4904 -4902 + -4900 -4897 -4894 -4892 -4889 -4886 -4884 -4881 + -4879 -4876 -4873 -4871 -4869 -4865 -4863 -4861 + -4858 -4855 -4853 -4850 -4848 -4845 -4842 -4840 + -4838 -4834 -4832 -4830 -4827 -4824 -4822 -4819 + -4816 -4814 -4812 -4809 -4806 -4804 -4801 -4798 + -4796 -4793 -4791 -4788 -4786 -4783 -4781 -4778 + -4775 -4773 -4770 -4767 -4765 -4763 -4760 -4757 + -4755 -4752 -4749 -4747 -4745 -4742 -4739 -4737 + -4735 -4732 -4729 -4727 -4724 -4721 -4719 -4716 + -4714 -4711 -4709 -4706 -4703 -4701 -4698 -4696 + -4693 -4691 -4688 -4686 -4683 -4680 -4678 -4676 + -4672 -4670 -4668 -4665 -4662 -4660 -4658 -4655 + -4652 -4650 -4647 -4645 -4642 -4640 -4637 -4635 + -4632 -4629 -4627 -4625 -4622 -4619 -4617 -4614 + -4612 -4609 -4606 -4604 -4602 -4599 -4596 -4594 + -4592 -4589 -4586 -4584 -4581 -4579 -4576 -4573 + -4571 -4569 -4566 -4563 -4561 -4558 -4556 -4553 + -4550 -4548 -4546 -4543 -4540 -4538 -4535 -4533 + -4530 -4527 -4525 -4523 -4520 -4518 -4515 -4512 + -4510 -4508 -4505 -4502 -4500 -4497 -4495 -4493 + -4490 -4487 -4485 -4482 -4480 -4477 -4474 -4472 + -4470 -4467 -4465 -4462 -4459 -4457 -4455 -4452 + -4449 -4446 -4444 -4442 -4439 -4437 -4434 -4431 + -4429 -4427 -4424 -4422 -4419 -4416 -4414 -4412 + -4409 -4407 -4404 -4401 -4399 -4396 -4394 -4392 + -4389 -4386 -4384 -4381 -4379 -4376 -4374 -4371 + -4368 -4366 -4364 -4361 -4359 -4356 -4353 -4351 + -4348 -4346 -4344 -4341 -4338 -4336 -4333 -4331 + -4328 -4326 -4324 -4321 -4318 -4315 -4313 -4311 + -4308 -4306 -4303 -4300 -4298 -4295 -4293 -4291 + -4288 -4286 -4283 -4280 -4278 -4275 -4273 -4270 + -4268 -4266 -4263 -4261 -4258 -4255 -4253 -4250 + -4248 -4245 -4243 -4241 -4238 -4235 -4232 -4230 + -4228 -4225 -4223 -4220 -4218 -4215 -4213 -4210 + -4207 -4205 -4202 -4200 -4198 -4195 -4193 -4190 + -4187 -4185 -4182 -4180 -4177 -4175 -4172 -4170 + -4168 -4165 -4163 -4160 -4157 -4155 -4152 -4150 + -4147 -4145 -4142 -4140 -4138 -4135 -4133 -4130 + -4127 -4124 -4122 -4119 -4117 -4115 -4112 -4110 + -4107 -4105 -4102 -4100 -4097 -4094 -4092 -4089 + -4087 -4084 -4082 -4079 -4077 -4075 -4072 -4070 + -4067 -4065 -4062 -4060 -4057 -4054 -4052 -4049 + -4047 -4044 -4042 -4039 -4037 -4034 -4032 -4029 + -4027 -4024 -4022 -4020 -4017 -4015 -4012 -4009 + -4006 -4004 -4001 -3999 -3996 -3994 -3991 -3989 + -3987 -3984 -3982 -3979 -3977 -3974 -3972 -3969 + -3967 -3964 -3962 -3959 -3957 -3954 -3952 -3949 + -3947 -3944 -3941 -3939 -3936 -3933 -3931 -3929 + -3926 -3924 -3921 -3919 -3916 -3914 -3911 -3909 + -3906 -3904 -3901 -3899 -3896 -3894 -3891 -3889 + -3886 -3884 -3881 -3879 -3876 -3874 -3871 -3869 + -3866 -3864 -3861 -3859 -3856 -3854 -3851 -3849 + -3846 -3844 -3841 -3839 -3836 -3833 -3831 -3828 + -3826 -3823 -3821 -3818 -3816 -3813 -3811 -3808 + -3806 -3803 -3801 -3798 -3796 -3793 -3791 -3788 + -3786 -3783 -3781 -3778 -3775 -3773 -3770 -3768 + -3765 -3763 -3760 -3758 -3755 -3753 -3750 -3748 + -3745 -3743 -3740 -3737 -3735 -3732 -3730 -3727 + -3725 -3722 -3720 -3717 -3715 -3712 -3709 -3707 + -3704 -3702 -3700 -3697 -3695 -3692 -3689 -3687 + -3685 -3682 -3680 -3677 -3675 -3672 -3669 -3667 + -3664 -3662 -3659 -3657 -3654 -3652 -3649 -3646 + -3644 -3641 -3639 -3636 -3634 -3631 -3629 -3626 + -3624 -3621 -3619 -3616 -3614 -3611 -3609 -3606 + -3604 -3601 -3598 -3596 -3593 -3591 -3588 -3585 + -3583 -3580 -3578 -3575 -3573 -3571 -3568 -3566 + -3563 -3561 -3558 -3555 -3553 -3550 -3548 -3545 + -3542 -3540 -3537 -3535 -3532 -3530 -3528 -3525 + -3522 -3520 -3517 -3515 -3512 -3509 -3507 -3504 + -3502 -3499 -3496 -3494 -3492 -3489 -3487 -3484 + -3482 -3479 -3476 -3474 -3471 -3469 -3466 -3463 + -3461 -3458 -3456 -3454 -3451 -3448 -3446 -3443 + -3441 -3438 -3435 -3433 -3430 -3428 -3425 -3423 + -3420 -3418 -3415 -3413 -3410 -3407 -3405 -3402 + -3399 -3397 -3395 -3392 -3390 -3387 -3384 -3382 + -3379 -3376 -3374 -3371 -3369 -3367 -3364 -3361 + -3359 -3356 -3353 -3351 -3348 -3345 -3343 -3341 + -3338 -3336 -3333 -3330 -3328 -3325 -3322 -3320 + -3318 -3315 -3313 -3310 -3307 -3305 -3302 -3299 + -3297 -3294 -3292 -3290 -3287 -3284 -3281 -3279 + -3276 -3273 -3271 -3269 -3266 -3264 -3261 -3258 + -3256 -3253 -3250 -3248 -3246 -3243 -3240 -3238 + -3235 -3232 -3229 -3227 -3225 -3222 -3220 -3217 + -3214 -3212 -3209 -3206 -3204 -3202 -3199 -3196 + -3194 -3191 -3188 -3186 -3184 -3181 -3178 -3175 + -3173 -3170 -3167 -3165 -3163 -3160 -3157 -3155 + -3152 -3149 -3147 -3144 -3142 -3139 -3136 -3134 + -3131 -3128 -3126 -3124 -3121 -3118 -3115 -3113 + -3110 -3108 -3105 -3103 -3100 -3097 -3094 -3092 + -3090 -3087 -3084 -3082 -3079 -3076 -3074 -3071 + -3069 -3066 -3063 -3061 -3058 -3056 -3053 -3050 + -3048 -3045 -3042 -3040 -3037 -3035 -3032 -3029 + -3026 -3024 -3021 -3019 -3016 -3014 -3011 -3008 + -3005 -3003 -3001 -2998 -2995 -2992 -2990 -2987 + -2985 -2982 -2979 -2976 -2974 -2972 -2969 -2966 + -2963 -2961 -2958 -2956 -2953 -2950 -2948 -2945 + -2942 -2940 -2937 -2935 -2932 -2929 -2926 -2924 + -2921 -2919 -2916 -2913 -2911 -2908 -2905 -2903 + -2900 -2897 -2895 -2892 -2890 -2887 -2884 -2881 + -2879 -2876 -2873 -2871 -2868 -2865 -2863 -2860 + -2857 -2855 -2852 -2850 -2847 -2844 -2841 -2839 + -2836 -2834 -2831 -2828 -2825 -2823 -2820 -2818 + -2815 -2812 -2809 -2807 -2804 -2801 -2799 -2796 + -2794 -2791 -2788 -2785 -2782 -2780 -2778 -2775 + -2772 -2769 -2767 -2764 -2761 -2758 -2756 -2753 + -2751 -2748 -2745 -2742 -2740 -2737 -2734 -2732 + -2729 -2726 -2724 -2721 -2718 -2715 -2713 -2710 + -2707 -2705 -2702 -2700 -2697 -2694 -2691 -2689 + -2686 -2683 -2680 -2677 -2675 -2673 -2670 -2667 + -2664 -2662 -2659 -2656 -2653 -2650 -2648 -2645 + -2642 -2639 -2637 -2635 -2632 -2629 -2626 -2623 + -2621 -2618 -2615 -2612 -2610 -2607 -2604 -2601 + -2599 -2596 -2593 -2590 -2588 -2586 -2583 -2580 + -2577 -2575 -2572 -2569 -2566 -2563 -2561 -2558 + -2555 -2552 -2550 -2547 -2544 -2541 -2539 -2536 + -2533 -2530 -2528 -2525 -2522 -2519 -2516 -2514 + -2511 -2508 -2505 -2503 -2500 -2497 -2494 -2492 + -2489 -2486 -2483 -2481 -2478 -2475 -2472 -2470 + -2467 -2464 -2461 -2458 -2456 -2453 -2450 -2447 + -2445 -2442 -2439 -2436 -2434 -2431 -2428 -2425 + -2423 -2420 -2417 -2414 -2412 -2409 -2406 -2403 + -2401 -2398 -2395 -2392 -2390 -2387 -2384 -2381 + -2379 -2375 -2372 -2369 -2367 -2364 -2361 -2358 + -2356 -2353 -2350 -2347 -2345 -2342 -2339 -2336 + -2334 -2331 -2327 -2325 -2322 -2319 -2316 -2314 + -2311 -2308 -2305 -2303 -2300 -2297 -2294 -2291 + -2288 -2285 -2282 -2280 -2277 -2274 -2271 -2269 + -2266 -2263 -2260 -2257 -2254 -2251 -2249 -2246 + -2243 -2240 -2238 -2235 -2231 -2229 -2226 -2223 + -2220 -2218 -2215 -2212 -2209 -2206 -2203 -2200 + -2198 -2195 -2192 -2189 -2186 -2183 -2180 -2178 + -2175 -2172 -2169 -2166 -2163 -2160 -2158 -2155 + -2152 -2149 -2146 -2143 -2140 -2137 -2135 -2132 + -2129 -2126 -2123 -2120 -2118 -2115 -2112 -2108 + -2106 -2103 -2100 -2097 -2094 -2091 -2088 -2086 + -2083 -2080 -2077 -2074 -2071 -2068 -2066 -2062 + -2059 -2057 -2054 -2051 -2048 -2045 -2042 -2039 + -2037 -2033 -2030 -2028 -2025 -2022 -2019 -2016 + -2013 -2010 -2008 -2004 -2001 -1999 -1996 -1992 + -1990 -1987 -1984 -1981 -1978 -1975 -1972 -1970 + -1966 -1963 -1960 -1958 -1954 -1952 -1949 -1946 + -1942 -1940 -1937 -1934 -1931 -1928 -1925 -1922 + -1919 -1916 -1913 -1910 -1907 -1904 -1902 -1898 + -1895 -1893 -1890 -1886 -1884 -1881 -1877 -1875 + -1872 -1869 -1866 -1863 -1860 -1857 -1854 -1851 + -1848 -1845 -1842 -1839 -1836 -1833 -1830 -1827 + -1824 -1821 -1818 -1815 -1812 -1809 -1806 -1803 + -1801 -1797 -1794 -1792 -1788 -1785 -1783 -1779 + -1776 -1774 -1770 -1767 -1764 -1761 -1758 -1755 + -1752 -1749 -1746 -1743 -1740 -1737 -1734 -1731 + -1728 -1725 -1722 -1719 -1716 -1712 -1710 -1707 + -1703 -1701 -1698 -1694 -1692 -1688 -1685 -1683 + -1679 -1676 -1674 -1670 -1667 -1664 -1661 -1658 + -1655 -1652 -1649 -1646 -1643 -1640 -1637 -1633 + -1631 -1627 -1624 -1621 -1618 -1615 -1612 -1609 + -1606 -1603 -1600 -1596 -1594 -1590 -1587 -1584 + -1581 -1578 -1575 -1572 -1569 -1566 -1562 -1560 + -1556 -1553 -1551 -1547 -1544 -1541 -1538 -1535 + -1532 -1528 -1526 -1522 -1519 -1516 -1513 -1509 + -1507 -1503 -1500 -1498 -1494 -1491 -1488 -1485 + -1482 -1479 -1475 -1473 -1469 -1466 -1463 -1460 + -1457 -1454 -1450 -1447 -1444 -1441 -1438 -1434 + -1432 -1428 -1425 -1422 -1419 -1415 -1413 -1409 + -1406 -1403 -1400 -1397 -1393 -1390 -1387 -1384 + -1381 -1378 -1374 -1372 -1368 -1365 -1362 -1358 + -1355 -1352 -1349 -1346 -1342 -1339 -1336 -1333 + -1330 -1327 -1323 -1320 -1317 -1314 -1311 -1307 + -1304 -1301 -1298 -1295 -1291 -1288 -1285 -1281 + -1279 -1275 -1272 -1269 -1265 -1262 -1259 -1256 + -1253 -1249 -1246 -1243 -1239 -1236 -1233 -1230 + -1226 -1223 -1220 -1217 -1214 -1210 -1207 -1204 + -1200 -1197 -1194 -1190 -1188 -1184 -1181 -1178 + -1174 -1171 -1168 -1165 -1161 -1158 -1155 -1151 + -1148 -1145 -1141 -1138 -1135 -1132 -1128 -1125 + -1122 -1118 -1115 -1112 -1108 -1105 -1102 -1099 + -1095 -1092 -1089 -1085 -1082 -1078 -1075 -1072 + -1068 -1066 -1062 -1059 -1055 -1052 -1049 -1045 + -1042 -1038 -1035 -1032 -1028 -1025 -1022 -1019 + -1015 -1012 -1009 -1005 -1002 -998 -995 -992 + -988 -985 -981 -978 -975 -971 -968 -964 + -961 -958 -955 -951 -947 -944 -941 -938 + -934 -931 -927 -924 -921 -917 -914 -910 + -907 -903 -900 -897 -893 -890 -886 -883 + -879 -876 -873 -869 -866 -862 -859 -855 + -852 -849 -845 -842 -838 -835 -831 -828 + -824 -821 -818 -814 -811 -807 -804 -800 + -797 -793 -790 -786 -782 -779 -776 -773 + -769 -765 -762 -758 -755 -751 -748 -744 + -741 -737 -734 -730 -727 -723 -719 -716 + -712 -709 -705 -702 -698 -695 -691 -688 + -685 -681 -678 -674 -671 -667 -664 -659 + -656 -652 -649 -645 -642 -638 -634 -631 + -627 -624 -620 -617 -613 -610 -606 -603 + -599 -595 -592 -588 -585 -581 -578 -574 + -570 -566 -563 -559 -556 -552 -549 -545 + -541 -538 -534 -531 -527 -523 -519 -516 + -512 -509 -505 -502 -498 -494 -490 -487 + -483 -480 -476 -472 -468 -465 -461 -457 + -454 -450 -447 -443 -439 -435 -432 -428 + -425 -420 -417 -413 -410 -406 -402 -398 + -395 -391 -388 -383 -380 -376 -373 -369 + -365 -361 -358 -354 -350 -346 -343 -339 + -336 -331 -328 -324 -320 -316 -313 -309 + -305 -301 -298 -294 -290 -286 -283 -279 + -275 -271 -268 -263 -260 -256 -253 -248 + -245 -241 -237 -233 -230 -225 -222 -218 + -214 -210 -207 -202 -199 -195 -191 -187 + -184 -179 -176 -171 -168 -164 -160 -156 + -153 -149 -145 -141 -137 -133 -129 -126 + -121 -118 -114 -110 -106 -102 -98 -95 + -90 -87 -82 -79 -75 -71 -67 -63 + -59 -55 -52 -47 -44 -39 -36 -31 + -28 -23 -20 -15 -12 -8 -4 0 + 3 8 11 16 19 23 27 31 + 35 39 43 47 51 55 59 63 + 67 71 75 79 83 87 91 96 + 99 104 107 112 115 119 124 127 + 132 135 140 144 148 152 156 160 + 164 168 172 177 180 185 189 193 + 197 201 205 209 214 217 222 226 + 230 234 238 243 246 251 255 259 + 263 267 272 275 280 284 288 292 + 296 301 304 309 313 317 322 325 + 330 334 338 342 346 351 355 360 + 363 368 372 376 381 384 389 393 + 397 402 406 410 414 419 423 427 + 432 436 440 444 448 453 457 462 + 466 470 475 479 483 487 492 496 + 500 505 509 513 518 522 527 531 + 535 540 544 549 553 557 561 565 + 570 574 579 583 587 592 596 601 + 606 610 614 619 623 628 632 636 + 641 645 650 654 658 663 667 672 + 676 681 686 690 694 699 703 708 + 712 716 722 726 730 735 739 744 + 748 753 758 762 766 771 775 780 + 785 789 794 798 803 808 812 816 + 821 826 830 835 839 844 849 853 + 858 863 867 872 876 881 886 890 + 895 900 904 909 914 918 923 928 + 932 937 942 946 951 956 960 965 + 970 974 979 984 989 994 998 1003 + 1008 1013 1017 1022 1027 1031 1037 1041 + 1046 1051 1056 1060 1065 1070 1074 1079 + 1084 1089 1094 1099 1103 1108 1113 1118 + 1123 1128 1132 1137 1143 1147 1152 1157 + 1162 1166 1171 1177 1181 1186 1191 1196 + 1201 1206 1211 1215 1221 1226 1230 1236 + 1241 1245 1250 1256 1260 1265 1271 1275 + 1280 1285 1290 1295 1300 1305 1310 1315 + 1320 1325 1330 1335 1341 1345 1350 1356 + 1361 1365 1370 1376 1381 1386 1391 1396 + 1401 1406 1412 1417 1421 1426 1432 1437 + 1442 1447 1453 1458 1462 1468 1473 1478 + 1483 1489 1494 1499 1504 1510 1515 1520 + 1525 1530 1535 1540 1546 1551 1556 1561 + 1567 1572 1577 1582 1588 1594 1599 1604 + 1609 1615 1620 1625 1630 1636 1641 1646 + 1651 1657 1663 1668 1673 1679 1684 1689 + 1694 1700 1706 1711 1716 1721 1727 1733 + 1738 1743 1749 1754 1760 1765 1770 1776 + 1782 1787 1792 1798 1804 1809 1814 1820 + 1825 1831 1837 1842 1847 1853 1859 1864 + 1870 1875 1881 1887 1892 1897 1903 1909 + 1915 1920 1926 1931 1937 1943 1948 1954 + 1959 1965 1971 1977 1982 1988 1993 1999 + 2005 2011 2016 2022 2028 2034 2040 2045 + 2051 2056 2062 2068 2074 2080 2085 2091 + 2097 2103 2109 2115 2120 2126 2132 2138 + 2144 2150 2155 2161 2167 2173 2179 2185 + 2191 2197 2202 2208 2214 2221 2227 2232 + 2238 2244 2250 2256 2263 2268 2274 2280 + 2286 2292 2298 2304 2311 2317 2323 2329 + 2334 2340 2346 2353 2359 2365 2371 2377 + 2383 2389 2395 2402 2408 2414 2421 2427 + 2433 2439 2445 2451 2457 2463 2470 2476 + 2483 2489 2495 2501 2507 2514 2520 2526 + 2532 2538 2545 2552 2558 2564 2571 2577 + 2583 2590 2596 2602 2608 2615 2621 2628 + 2634 2641 2648 2654 2660 2667 2673 2680 + 2686 2692 2699 2705 2712 2718 2725 2731 + 2738 2744 2751 2758 2764 2771 2778 2784 + 2791 2798 2804 2811 2818 2824 2831 2838 + 2844 2851 2858 2864 2871 2878 2884 2891 + 2898 2905 2911 2918 2925 2932 2938 2945 + 2952 2959 2966 2973 2979 2986 2993 3000 + 3007 3014 3021 3028 3035 3042 3049 3056 + 3063 3070 3077 3084 3091 3097 3104 3111 + 3118 3125 3132 3139 3146 3153 3161 3168 + 3175 3182 3189 3196 3204 3211 3218 3225 + 3233 3240 3247 3254 3261 3268 3275 3283 + 3290 3297 3305 3312 3320 3327 3334 3341 + 3348 3356 3363 3371 3378 3386 3393 3401 + 3408 3415 3422 3430 3437 3445 3453 3460 + 3468 3475 3482 3490 3498 3505 3513 3521 + 3528 3536 3543 3551 3558 3566 3574 3582 + 3589 3597 3604 3612 3620 3628 3636 3643 + 3651 3659 3667 3675 3683 3690 3698 3706 + 3714 3722 3730 3737 3745 3753 3762 3770 + 3777 3785 3793 3801 3809 3817 3825 3833 + 3842 3850 3857 3866 3874 3882 3890 3898 + 3906 3915 3923 3931 3939 3948 3956 3964 + 3972 3981 3989 3997 4005 4014 4022 4030 + 4039 4047 4055 4064 4072 4081 4089 4098 + 4106 4115 4123 4132 4141 4148 4157 4166 + 4175 4183 4192 4201 4209 4218 4227 4235 + 4244 4253 4261 4270 4279 4287 4296 4305 + 4313 4323 4332 4340 4349 4358 4367 4376 + 4385 4394 4403 4411 4421 4430 4438 4448 + 4457 4466 4475 4484 4493 4502 4511 4521 + 4529 4539 4548 4557 4567 4576 4585 4594 + 4604 4613 4622 4632 4641 4651 4660 4669 + 4679 4688 4698 4707 4717 4726 4736 4745 + 4755 4764 4774 4783 4793 4803 4813 4822 + 4832 4841 4852 4861 4871 4881 4891 4900 + 4911 4920 4930 4940 4950 4960 4970 4980 + 4990 5000 5010 5020 5030 5040 5050 5060 + 5071 5080 5091 5101 5112 5122 5131 5142 + 5152 5163 5173 5183 5194 5204 5215 5225 + 5235 5246 5257 5267 5278 5288 5299 5310 + 5320 5330 5342 5352 5363 5374 5384 5395 + 5406 5417 5427 5438 5450 5460 5471 5482 + 5493 5504 5515 5526 5537 5548 5559 5571 + 5582 5593 5604 5615 5627 5638 5649 5660 + 5672 5683 5695 5706 5717 5729 5741 5752 + 5763 5775 5786 5798 5810 5822 5833 5845 + 5856 5868 5880 5892 5904 5916 5928 5940 + 5951 5963 5975 5987 5999 6011 6024 6036 + 6048 6060 6073 6085 6097 6109 6122 6134 + 6146 6159 6171 6184 6196 6209 6221 6234 + 6246 6259 6272 6285 6297 6309 6322 6335 + 6348 6361 6374 6387 6400 6413 6426 6439 + 6451 6465 6478 6491 6505 6518 6531 6544 + 6557 6571 6584 6598 6611 6624 6638 6652 + 6666 6679 6693 6706 6720 6734 6747 6762 + 6776 6789 6803 6817 6831 6845 6859 6874 + 6887 6902 6916 6930 6945 6959 6973 6988 + 7002 7017 7031 7046 7061 7075 7090 7104 + 7119 7134 7149 7164 7178 7194 7208 7224 + 7238 7254 7269 7284 7299 7314 7330 7345 + 7361 7376 7391 7407 7422 7438 7454 7470 + 7485 7501 7517 7533 7548 7565 7581 7596 + 7612 7629 7645 7661 7677 7694 7710 7726 + 7743 7760 7776 7793 7809 7826 7843 7860 + 7877 7894 7911 7927 7944 7961 7979 7996 + 8013 8031 8048 8066 8083 8100 8118 8135 + 8153 8171 8189 8207 8225 8243 8261 8279 + 8297 8315 8333 8351 8370 8389 8407 8425 + 8444 8463 8482 8500 8519 8538 8557 8576 + 8595 8615 8633 8653 8673 8692 8711 8731 + 8750 8770 8790 8810 8829 8850 8869 8890 + 8910 8930 8950 8970 8991 9012 9032 9053 + 9073 9094 9116 9136 9157 9178 9200 9221 + 9242 9263 9285 9307 9328 9350 9372 9393 + 9415 9437 9459 9482 9504 9526 9549 9571 + 9594 9617 9640 9663 9686 9709 9732 9755 + 9778 9802 9826 9849 9873 9897 9920 9945 + 9969 9993 10017 10041 10066 10090 10115 10140 + 10165 10190 10215 10240 10265 10291 10316 10342 + 10368 10394 10419 10445 10471 10498 10525 10551 + 10578 10604 10631 10658 10685 10713 10740 10767 + 10795 10822 10850 10879 10907 10934 10963 10991 + 11020 11049 11077 11107 11136 11165 11194 11224 + 11254 11284 11314 11344 11374 11405 11435 11466 + 11496 11527 11558 11590 11621 11653 11685 11717 + 11749 11782 11813 11846 11879 11912 11945 11979 + 12012 12046 12079 12114 12148 12182 12217 12252 + 12287 12321 12357 12392 12428 12464 12500 12536 + 12573 12610 12647 12684 12722 12759 12797 12835 + 12873 12911 12950 12989 13028 13067 13107 13147 + 13187 13227 13268 13309 13350 13392 13433 13475 + 13517 13560 13602 13646 13689 13732 13776 13820 + 13864 13909 13954 14000 14045 14091 14138 14184 + 14231 14278 14325 14373 14421 14470 14519 14568 + 14618 14668 14718 14769 14820 14871 14923 14976 + 15028 15081 15134 15188 15243 15297 15353 15408 + 15464 15520 15577 15634 15692 15751 15809 15869 + 15929 15989 16050 16111 16173 16235 16298 16361 + 16426 16490 16556 16621 16688 16755 16823 16891 + 16960 17029 17099 17170 17242 17315 17387 17461 + 17535 17610 17687 17764 17841 17919 17999 18079 + 18159 18241 18323 18407 18492 18577 18663 18751 + 18839 18928 19019 19110 19203 19297 19392 19488 + 19585 19683 19783 19884 19986 20090 20195 20301 + 20408 20518 20628 20740 20855 20970 21086 21206 + 21326 21448 21572 21698 21826 21956 22088 22222 + 22358 22496 22638 22780 22926 23074 23225 23378 + 23534 23693 23854 24019 24187 24359 24533 24710 + 24893 25078 25266 25460 25658 25859 26065 26276 + 26492 26712 26939 27170 27408 27652 27901 28157 + 28421 28691 28969 29256 29286 29304 29322 29341 + 29360 29379 29399 29419 29440 29462 29483 29506 + 29529 29553 29577 29602 29628 29655 29682 29711 + 29741 29771 29803 29837 29871 29908 29946 29986 + 30028 30073 30120 30171 30225 30284 30348 30417 + 30495 30582 30681 30797 30937 31116 31365 31789 diff --git a/tc/Makefile b/tc/Makefile new file mode 100644 index 0000000..06546f9 --- /dev/null +++ b/tc/Makefile @@ -0,0 +1,74 @@ +TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o \ + m_police.o m_estimator.o m_action.o + +include ../Config + +TCMODULES := +TCMODULES += q_fifo.o +TCMODULES += q_sfq.o +TCMODULES += q_red.o +TCMODULES += q_prio.o +TCMODULES += q_tbf.o +TCMODULES += q_cbq.o +TCMODULES += f_rsvp.o +TCMODULES += f_u32.o +TCMODULES += f_route.o +TCMODULES += f_fw.o +TCMODULES += q_dsmark.o +TCMODULES += q_gred.o +TCMODULES += f_tcindex.o +TCMODULES += q_ingress.o +TCMODULES += q_hfsc.o +TCMODULES += q_htb.o +TCMODULES += m_gact.o +TCMODULES += m_mirred.o +TCMODULES += m_ipt.o +TCMODULES += m_pedit.o +TCMODULES += p_ip.o +TCMODULES += p_icmp.o +TCMODULES += p_tcp.o +TCMODULES += p_udp.o + +TCOBJ += $(TCMODULES) + +TCLIB := tc_core.o +TCLIB += tc_red.o +TCLIB += tc_cbq.o +TCLIB += tc_estimator.o + +CFLAGS += -DCONFIG_GACT -DCONFIG_GACT_PROB + +TCSO := +TCSO += q_netem.so +ifeq ($(TC_CONFIG_ATM),y) + TCSO += q_atm.so +endif + +LDLIBS += -L. -ltc -lm -ldl + +LDFLAGS += -Wl,-export-dynamic + +%.so: %.c + $(CC) $(CFLAGS) -shared -fpic $< -o $@ + + +all: libtc.a tc $(TCSO) + +tc: $(TCOBJ) $(LIBNETLINK) $(LIBUTIL) $(TCLIB) + +libtc.a: $(TCLIB) + $(AR) rcs $@ $(TCLIB) + +install: all + mkdir -p $(DESTDIR)/usr/lib/tc + install -m 0755 -s tc $(DESTDIR)$(SBINDIR) + for i in $(TCSO); \ + do install -m 755 -s $$i $(DESTDIR)/usr/lib/tc; \ + done + +clean: + rm -f $(TCOBJ) $(TCLIB) libtc.a tc *.so + +q_atm.so: q_atm.c + $(CC) $(CFLAGS) -shared -fpic -o q_atm.so q_atm.c -latm + diff --git a/tc/README.last b/tc/README.last new file mode 100644 index 0000000..9400438 --- /dev/null +++ b/tc/README.last @@ -0,0 +1,47 @@ +Kernel code and interface. +-------------------------- + +* Compile time switches + +There is only one, but very important, compile time switch. +It is not settable by "make config", but should be selected +manually and after a bit of thinking in <include/net/pkt_sched.h> + +PSCHED_CLOCK_SOURCE can take three values: + + PSCHED_GETTIMEOFDAY + PSCHED_JIFFIES + PSCHED_CPU + + + PSCHED_GETTIMEOFDAY + +Default setting is the most conservative PSCHED_GETTIMEOFDAY. +It is very slow both because of weird slowness of do_gettimeofday() +and because it forces code to use unnatural "timeval" format, +where microseconds and seconds fields are separate. +Besides that, it will misbehave, when delays exceed 2 seconds +(f.e. very slow links or classes bounded to small slice of bandwidth) +To resume: as only you will get it working, select correct clock +source and forget about PSCHED_GETTIMEOFDAY forever. + + + PSCHED_JIFFIES + +Clock is derived from jiffies. On architectures with HZ=100 +granularity of this clock is not enough to make reasonable +bindings to real time. However, taking into account Linux +architecture problems, which force us to use artificial +integrated clock in any case, this switch is not so bad +for schduling even on high speed networks, though policing +is not reliable. + + + PSCHED_CPU + +It is available only for alpha and pentiums with correct +CPU timestamp. It is the fastest way, use it when it is available, +but remember: not all pentiums have this facility, and +a lot of them have clock, broken by APM etc. etc. + + diff --git a/tc/f_fw.c b/tc/f_fw.c new file mode 100644 index 0000000..5a56098 --- /dev/null +++ b/tc/f_fw.c @@ -0,0 +1,140 @@ +/* + * f_fw.c FW filter. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 <linux/if.h> /* IFNAMSIZ */ +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... fw [ classid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " CLASSID := X:Y\n"); +} + +#define usage() return(-1) + +static int fw_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "classid") == 0 || + matches(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4); + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_FW_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "action") == 0) { + NEXT_ARG(); + if (parse_action(&argc, &argv, TCA_FW_ACT, n)) { + fprintf(stderr, "Illegal fw \"action\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "indev") == 0) { + char d[IFNAMSIZ+1]; + memset(d, 0, sizeof (d)); + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "Illegal indev\n"); + return -1; + } + strncpy(d, *argv, sizeof (d) - 1); + addattr_l(n, MAX_MSG, TCA_FW_INDEV, d, strlen(d) + 1); + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int fw_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_FW_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_FW_MAX, opt); + + if (handle) + fprintf(f, "handle 0x%x ", handle); + + if (tb[TCA_FW_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "classid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_FW_CLASSID]), b1)); + } + + if (tb[TCA_FW_POLICE]) + tc_print_police(f, tb[TCA_FW_POLICE]); + if (tb[TCA_FW_INDEV]) { + struct rtattr *idev = tb[TCA_FW_INDEV]; + fprintf(f, "input dev %s ",(char *)RTA_DATA(idev)); + } + + if (tb[TCA_FW_ACT]) { + fprintf(f, "\n"); + tc_print_action(f, tb[TCA_FW_ACT]); + } + return 0; +} + +struct filter_util fw_filter_util = { + .id = "fw", + .parse_fopt = fw_parse_opt, + .print_fopt = fw_print_opt, +}; diff --git a/tc/f_fw.o b/tc/f_fw.o new file mode 100644 index 0000000000000000000000000000000000000000..a3d379800ee0a77d5d79eb0c750f915a568194f4 GIT binary patch literal 5408 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 SG!j+#<GhdOFq+U8I==x&bPizv literal 0 HcmV?d00001 diff --git a/tc/f_route.c b/tc/f_route.c new file mode 100644 index 0000000..a41b9d5 --- /dev/null +++ b/tc/f_route.c @@ -0,0 +1,169 @@ +/* + * f_route.c ROUTE filter. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 "rt_names.h" +#include "tc_common.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... route [ from REALM | fromif TAG ] [ to REALM ]\n"); + fprintf(stderr, " [ flowid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " CLASSID := X:Y\n"); +} + +#define usage() return(-1) + +static int route_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + __u32 fh = 0xFFFF8000; + __u32 order = 0; + + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "to") == 0) { + __u32 id; + NEXT_ARG(); + if (rtnl_rtrealm_a2n(&id, *argv)) { + fprintf(stderr, "Illegal \"to\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_TO, &id, 4); + fh &= ~0x80FF; + fh |= id&0xFF; + } else if (matches(*argv, "from") == 0) { + __u32 id; + NEXT_ARG(); + if (rtnl_rtrealm_a2n(&id, *argv)) { + fprintf(stderr, "Illegal \"from\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_FROM, &id, 4); + fh &= 0xFFFF; + fh |= id<<16; + } else if (matches(*argv, "fromif") == 0) { + __u32 id; + NEXT_ARG(); + ll_init_map(&rth); + if ((id=ll_name_to_index(*argv)) <= 0) { + fprintf(stderr, "Illegal \"fromif\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_IIF, &id, 4); + fh &= 0xFFFF; + fh |= (0x8000|id)<<16; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_ROUTE4_CLASSID, &handle, 4); + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_ROUTE4_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "order") == 0) { + NEXT_ARG(); + if (get_u32(&order, *argv, 0)) { + fprintf(stderr, "Illegal \"order\"\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++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + if (order) { + fh &= ~0x7F00; + fh |= (order<<8)&0x7F00; + } + if (!t->tcm_handle) + t->tcm_handle = fh; + return 0; +} + +static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_ROUTE4_MAX+1]; + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ROUTE4_MAX, opt); + + if (handle) + fprintf(f, "fh 0x%08x ", handle); + if (handle&0x7F00) + fprintf(f, "order %d ", (handle>>8)&0x7F); + + if (tb[TCA_ROUTE4_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_CLASSID]), b1)); + } + if (tb[TCA_ROUTE4_TO]) + fprintf(f, "to %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_TO]), b1, sizeof(b1))); + if (tb[TCA_ROUTE4_FROM]) + fprintf(f, "from %s ", rtnl_rtrealm_n2a(*(__u32*)RTA_DATA(tb[TCA_ROUTE4_FROM]), b1, sizeof(b1))); + if (tb[TCA_ROUTE4_IIF]) + fprintf(f, "fromif %s", ll_index_to_name(*(int*)RTA_DATA(tb[TCA_ROUTE4_IIF]))); + if (tb[TCA_ROUTE4_POLICE]) + tc_print_police(f, tb[TCA_ROUTE4_POLICE]); + return 0; +} + +struct filter_util route_filter_util = { + .id = "route", + .parse_fopt = route_parse_opt, + .print_fopt = route_print_opt, +}; diff --git a/tc/f_route.o b/tc/f_route.o new file mode 100644 index 0000000000000000000000000000000000000000..d2dc8ab2f16efc21a4496e2639f8ed8da60c434f GIT binary patch literal 6952 zcmbVQeQaA-6~Bqy*3CNGqiGu;u&3ZuDO;^LEu#t@i<_i<ncUGW39Pi4@to(x^!Y>f zi<V7Qlg{z9he@c!CdMD2N=T#9{;33XAsWT$$J((8q3{Q55n9C5tywE3*p^A7%$#%I zy~)XoUzxaS-h20Vf9H43J@?Zc>yGTLt*K$M)Ua=|<(fnpE7vW5cks^+=3^U}TDa*^ zOLrnN(+-cbX#9JuUdP90Xxa)i2dm91n$*Gt&jN}j5{VP?FZ}IL`Bu0;+_!)7d;b34 zvGGc!H)5VwkKa52OtrYEP96;$P|Zld^V@ZRn0*2FC3gU2K2TpWHug-8HiYJQti`gg z*ydua6bZOP<>G528`a|%Q034-_;7e2{MdQ$TXJ=W%Awb0Y5_mX{NTcTt59|o=5IwN z?`_~ZC2&!k^G*E(D3M7cps1y;fafgKARW7wT%G52XhD8tQuS2+P@Kc?A{eZ1>Sy3W zEoD8}fyO1PGl)(`0*&EmHwdhV<M=f;R;h5R?@U*qas5S<Rtw+*#8Dc;`cb-4sgzt+ z%xcY>iLPze6JfKPmE3`1+3?TKdyZeLKpLCb7-Q=%hB5eJd1Ou4)B_%okx+RlC)GMP zqH)xrV;5kG$c|YV!_0qzD0HkrbPNV!DZvm>MNBA&reSv8zo0-bs=6MbeYO(nn|c_; zLyHmfW0}W^uks+EYs*7ob_MEv6BQ9hy@&&|EC+gJ9QEUGjAJOze_A~hBNu_G<eA24 z$5+HH)tud5s)?Ade(<94T|OZq<|P?YaT9p17W{KyufjayGr_HzS7z1#vSc<T%ogs3 zIJifick^;jVb;UvlAKt__RF;Um#kYwcm1NfP+jUSCoN3>2V_O&<a=QjeBmlQ!8vl3 z--X7gu)G0G!#GtIesv2|WbG38w2^LHk&V78jjvQ2XJ7FQP;WQQ2KWrgdO~xm`L68m z#Cs$XSiSa079!lrinL017VahOpQ~cOfwWh%zbOZ48V7OpwAiec?uP~zG0*V{5-Ri1 zuRr%mrDElB=--%n+yU`lziNKeYrYQq-DR*+i*v?PzL)FN$yWm7STCG&pIr+dg$1{j zg1?TW7B7vB98*gZfoG9{YY%rS1gtF+04ui>ffpp>s$iUyfE(vc>DPf%99Z-fzk<N= z3m66N4zO49_(Em#s<ri*@AC-2yYckw4Ns`d;lkTvs`<WomABELCXPTRVZR-P8UGG% z6*A|5FS0^op1_f__^?(wW;!5{+nC9oO>6}-bn;n@PJ$*TY@!x@@&19SqnG9EO#EH0 z7YN?!DoGWn{?C%jj2PN&SpE6fkk+mQgF%H;l|xD*mq{s)bcZARm7~0pOelTfeaZl| z85z+sAkKBgR=`}(Jdqq!IwRrU-kvUQmCfkMxTYNVR-~u1JKB4oyHl~*)28<Oc{q9| zb3}<5O5dKnbxe4XqCk1H{a_t4G7O{Tk7R<y^;kZi9As7q%yP?mbX^;Y=}OaZEIp`e zO?AtNk)bt|f_f%5sO6S9ylST<6BH4CDP;KuWLVR)>^sBIkz`(JYR)%3P{$I(O6zEI z>#kA7a;7v7Dk5m5Ij=ys_=^t&*6{-dp*hc3y%@_HRx|3~u+DP=w@oOG;*VG0VTg|| zFYZ>1?S#MG{L6YQ;`sz+kJsPrZP?>ge(0WbP1SaLTb^07*V|fH`=Gas(s<*MTFA#k z7)#jef2Ov}+fZ=zc;^w@*V);wwCsDRf19!+xHH(Mw6(VG47G;7uCzR&4Js-mfaBZm z3nt<_wlNmWKbbOOkHg!@S?^)_mDBWC(9lK=7UZ9UF(bx;kLUAH&tL^@Lcv`tt0BSG zhNFpGETyqvI%8--2x)uXh{cbvU_6sbX=&OW-k1O9p@0!~8#hT>Iz*)lPj6DJQCJ4P z2%lmImO=RX6CMsw=7l|;DCoz7Ae08{#qNPO;&|Pm<)pA{p&)!KwmROPtNB0u1&i1! z_~;*f*!_nDX7_*D)^8!R%GZ6+fbq9Uf_O{&U)pf#A8oL7!(TP~j39a>k+Ur>eoFYD z_{RG<Eu-tL8t(J7NdFS+gO6(Yn-}?01rhUyw$fgXUmP$Qi=gtCpZs2;xSb1I!ENnv zd}y)T;~m33cE}G4|3|4YeB14@ZqFT>jC=73^HhjqRpx->_<(|GuIB#*3=GOHn?qaG zB2{@J&&~b`*Z$STwW=2u$5k~Fj1TWxE6W{Ftp%NN0n2?9*E7e3iQFfV=32E&SjH~4 zda>ZTMjct`X60I%MY7d;yIA!}<6;=?%CZBhcai?Kh-O?*_B{%lA`W=31AfQ>*BtPa z1Afc_f7Sv2sRRC^1O6KaeAWSf!vTNG0e{y4|Ih(n3qxKl|63gJ76+UqqWm5gj5Gd@ zW@EX$7R_W0S<fZY2Cr+QSv{6a3p|n34J{WPF_JpV8*l^4vBVR(BwQYcG$T5)qm9J| z;pjDTQJtk?MtoSyvz(FEqd6m|#q?A()&^1tWU^YC>3TE`XK>WWM3d=3ZIoGYh$|UN z8u6&SuK_ukP8!ivENe-`^-Nx4$uu0;SzR-<D4yJKdt-?#hMy2>aBqNsSe}Daa7AG0 zwit%P!@zhjE(YE$D-n@giMXfXo&`Vmc)|Zl!h$PIa<Go;9SidD$EV~sAqd5;Bllye z)4^Gc;W>!KuET%K37p1*>k*688Rs>|9u{xA&Z7jUIzs|S9eFk(bV=Z-gY!}L%Lnrf z3*xv%OC0|{1dG(!VpUli;p3G?@`nk&k>EcjIMrW=4xvbWalA60!0|=(!vv@LI>D*_ z(*l?JcXMxy{ebYP{%L~O16TIzBEdHi{6m6MzjunmPx{6C7?w=}M>FcT#l{bEHe=tg z;a?MYr-RN@1jo58<DVutj-kZ=MsOPcdj!WjjpYA};GZXWjkqjfzi<pC-$QUZKeq^6 z=12Z^X(W8Q?zR)0>g=}ZJOsY6gbAPOJV<b=bI_)9*pgw75kA%VzKyR7eva_z_?{v- zU3V`NoW}F2O+P2}UnhJT&tC{mb>6Y*6ok$V!lybP6P)U-755%FUe61iI|Pp7PIa~r zoa$_~>6{We_YgkSdBDb>5&S;Fr}~F&eEvUaU=t^N+}q{+e2L(V1aB5U{A7RsDEw|0 zIQEz7?6&c*2!4+Pe~92;B7Sw7&f7xgNy4Xoe@^f(6P;h!bp9c9P7^-W`7^<(&YL!! zkA%)O!lycJafo7maIcklUN3N&&rgNUX2PdBI|;53ovg64&o}wI3y&FW8y7r9yq9n> S-gaEjScjc2>Z5jC==>Lxo4%R= literal 0 HcmV?d00001 diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c new file mode 100644 index 0000000..13fcf97 --- /dev/null +++ b/tc/f_rsvp.c @@ -0,0 +1,404 @@ +/* + * q_rsvp.c RSVP filter. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 "rt_names.h" +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]\n"); + fprintf(stderr, " [ sender SRC[/PORT | GPI ]\n"); + fprintf(stderr, " [ classid CLASSID ] [ police POLICE_SPEC ]\n"); + fprintf(stderr, " [ tunnelid ID ] [ tunnel ID skip NUMBER ]\n"); + fprintf(stderr, "Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |\n"); + fprintf(stderr, " u{8|16|32} NUMBER mask MASK at OFFSET}\n"); + fprintf(stderr, " POLICE_SPEC := ... look at TBF\n"); + fprintf(stderr, " FILTERID := X:Y\n"); +} + +#define usage() return(-1) + +int get_addr_and_pi(int *argc_p, char ***argv_p, inet_prefix * addr, + struct tc_rsvp_pinfo *pinfo, int dir, int family) +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p = strchr(*argv, '/'); + struct tc_rsvp_gpi *pi = dir ? &pinfo->dpi : &pinfo->spi; + + if (p) { + __u16 tmp; + + if (get_u16(&tmp, p+1, 0)) + return -1; + + if (dir == 0) { + /* Source port: u16 at offset 0 */ + pi->key = htonl(((__u32)tmp)<<16); + pi->mask = htonl(0xFFFF0000); + } else { + /* Destination port: u16 at offset 2 */ + pi->key = htonl(((__u32)tmp)); + pi->mask = htonl(0x0000FFFF); + } + pi->offset = 0; + *p = 0; + } + if (get_addr_1(addr, *argv, family)) + return -1; + if (p) + *p = '/'; + + argc--; argv++; + + if (pi->mask || argc <= 0) + goto done; + + if (strcmp(*argv, "spi/ah") == 0 || + strcmp(*argv, "gpi/ah") == 0) { + __u32 gpi; + NEXT_ARG(); + if (get_u32(&gpi, *argv, 0)) + return -1; + pi->mask = htonl(0xFFFFFFFF); + pi->key = htonl(gpi); + pi->offset = 4; + if (pinfo->protocol == 0) + pinfo->protocol = IPPROTO_AH; + argc--; argv++; + } else if (strcmp(*argv, "spi/esp") == 0 || + strcmp(*argv, "gpi/esp") == 0) { + __u32 gpi; + NEXT_ARG(); + if (get_u32(&gpi, *argv, 0)) + return -1; + pi->mask = htonl(0xFFFFFFFF); + pi->key = htonl(gpi); + pi->offset = 0; + if (pinfo->protocol == 0) + pinfo->protocol = IPPROTO_ESP; + argc--; argv++; + } else if (strcmp(*argv, "flowlabel") == 0) { + __u32 flabel; + NEXT_ARG(); + if (get_u32(&flabel, *argv, 0)) + return -1; + if (family != AF_INET6) + return -1; + pi->mask = htonl(0x000FFFFF); + pi->key = htonl(flabel) & pi->mask; + pi->offset = -40; + argc--; argv++; + } else if (strcmp(*argv, "u32") == 0 || + strcmp(*argv, "u16") == 0 || + strcmp(*argv, "u8") == 0) { + int sz = 1; + __u32 tmp; + __u32 mask = 0xff; + if (strcmp(*argv, "u32") == 0) { + sz = 4; + mask = 0xffff; + } else if (strcmp(*argv, "u16") == 0) { + mask = 0xffffffff; + sz = 2; + } + NEXT_ARG(); + if (get_u32(&tmp, *argv, 0)) + return -1; + argc--; argv++; + if (strcmp(*argv, "mask") == 0) { + NEXT_ARG(); + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + } + if (strcmp(*argv, "at") == 0) { + NEXT_ARG(); + if (get_integer(&pi->offset, *argv, 0)) + return -1; + argc--; argv++; + } + if (sz == 1) { + if ((pi->offset & 3) == 0) { + mask <<= 24; + tmp <<= 24; + } else if ((pi->offset & 3) == 1) { + mask <<= 16; + tmp <<= 16; + } else if ((pi->offset & 3) == 3) { + mask <<= 8; + tmp <<= 8; + } + } else if (sz == 2) { + if ((pi->offset & 3) == 0) { + mask <<= 16; + tmp <<= 16; + } + } + pi->offset &= ~3; + pi->mask = htonl(mask); + pi->key = htonl(tmp) & pi->mask; + } + +done: + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +static int rsvp_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; + struct tc_rsvp_pinfo pinfo; + struct tc_police tp; + struct tcmsg *t = NLMSG_DATA(n); + int pinfo_ok = 0; + struct rtattr *tail; + + memset(&pinfo, 0, sizeof(pinfo)); + memset(&tp, 0, sizeof(tp)); + + if (handle) { + if (get_u32(&t->tcm_handle, handle, 0)) { + fprintf(stderr, "Illegal \"handle\"\n"); + return -1; + } + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, 4096, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "session") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 1, family)) { + fprintf(stderr, "Illegal \"session\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_DST, &addr.data, addr.bytelen); + if (pinfo.dpi.mask || pinfo.protocol) + pinfo_ok++; + continue; + } else if (matches(*argv, "sender") == 0 || + matches(*argv, "flowspec") == 0) { + inet_prefix addr; + NEXT_ARG(); + if (get_addr_and_pi(&argc, &argv, &addr, &pinfo, 0, family)) { + fprintf(stderr, "Illegal \"sender\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_SRC, &addr.data, addr.bytelen); + if (pinfo.spi.mask || pinfo.protocol) + pinfo_ok++; + continue; + } else if (matches("ipproto", *argv) == 0) { + int num; + NEXT_ARG(); + num = inet_proto_a2n(*argv); + if (num < 0) { + fprintf(stderr, "Illegal \"ipproto\"\n"); + return -1; + } + pinfo.protocol = num; + pinfo_ok++; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4); + } else if (strcmp(*argv, "tunnelid") == 0) { + unsigned tid; + NEXT_ARG(); + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"tunnelid\"\n"); + return -1; + } + pinfo.tunnelid = tid; + pinfo_ok++; + } else if (strcmp(*argv, "tunnel") == 0) { + unsigned tid; + NEXT_ARG(); + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"tunnel\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_RSVP_CLASSID, &tid, 4); + NEXT_ARG(); + if (strcmp(*argv, "skip") == 0) { + NEXT_ARG(); + } + if (get_unsigned(&tid, *argv, 0)) { + fprintf(stderr, "Illegal \"skip\"\n"); + return -1; + } + pinfo.tunnelhdr = tid; + pinfo_ok++; + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_RSVP_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (pinfo_ok) + addattr_l(n, 4096, TCA_RSVP_PINFO, &pinfo, sizeof(pinfo)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static char * sprint_spi(struct tc_rsvp_gpi *pi, int dir, char *buf) +{ + if (pi->offset == 0) { + if (dir && pi->mask == htonl(0xFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)); + return buf; + } + if (!dir && pi->mask == htonl(0xFFFF0000)) { + snprintf(buf, SPRINT_BSIZE-1, "/%d", htonl(pi->key)>>16); + return buf; + } + if (pi->mask == htonl(0xFFFFFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " spi/esp 0x%08x", htonl(pi->key)); + return buf; + } + } else if (pi->offset == 4 && pi->mask == htonl(0xFFFFFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " spi/ah 0x%08x", htonl(pi->key)); + return buf; + } else if (pi->offset == -40 && pi->mask == htonl(0x000FFFFF)) { + snprintf(buf, SPRINT_BSIZE-1, " flowlabel 0x%05x", htonl(pi->key)); + return buf; + } + snprintf(buf, SPRINT_BSIZE-1, " u32 0x%08x mask %08x at %d", + htonl(pi->key), htonl(pi->mask), pi->offset); + return buf; +} + +static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + int family = strcmp(qu->id, "rsvp") == 0 ? AF_INET : AF_INET6; + struct rtattr *tb[TCA_RSVP_MAX+1]; + struct tc_rsvp_pinfo *pinfo = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_RSVP_MAX, opt); + + if (handle) + fprintf(f, "fh 0x%08x ", handle); + + if (tb[TCA_RSVP_PINFO]) { + if (RTA_PAYLOAD(tb[TCA_RSVP_PINFO]) < sizeof(*pinfo)) + return -1; + + pinfo = RTA_DATA(tb[TCA_RSVP_PINFO]); + } + + if (tb[TCA_RSVP_CLASSID]) { + SPRINT_BUF(b1); + if (!pinfo || pinfo->tunnelhdr == 0) + fprintf(f, "flowid %s ", sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), b1)); + else + fprintf(f, "tunnel %d skip %d ", *(__u32*)RTA_DATA(tb[TCA_RSVP_CLASSID]), pinfo->tunnelhdr); + } else if (pinfo && pinfo->tunnelhdr) + fprintf(f, "tunnel [BAD] skip %d ", pinfo->tunnelhdr); + + if (tb[TCA_RSVP_DST]) { + char buf[128]; + fprintf(f, "session "); + if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_DST]), buf, sizeof(buf)) == 0) + fprintf(f, " [INVALID DADDR] "); + else + fprintf(f, "%s", buf); + if (pinfo && pinfo->dpi.mask) { + SPRINT_BUF(b2); + fprintf(f, "%s ", sprint_spi(&pinfo->dpi, 1, b2)); + } else + fprintf(f, " "); + } else { + if (pinfo && pinfo->dpi.mask) { + SPRINT_BUF(b2); + fprintf(f, "session [NONE]%s ", sprint_spi(&pinfo->dpi, 1, b2)); + } else + fprintf(f, "session NONE "); + } + + if (pinfo && pinfo->protocol) { + SPRINT_BUF(b1); + fprintf(f, "ipproto %s ", inet_proto_n2a(pinfo->protocol, b1, sizeof(b1))); + } + if (pinfo && pinfo->tunnelid) + fprintf(f, "tunnelid %d ", pinfo->tunnelid); + if (tb[TCA_RSVP_SRC]) { + char buf[128]; + fprintf(f, "sender "); + if (inet_ntop(family, RTA_DATA(tb[TCA_RSVP_SRC]), buf, sizeof(buf)) == 0) { + fprintf(f, "[BAD]"); + } else { + fprintf(f, " %s", buf); + } + if (pinfo && pinfo->spi.mask) { + SPRINT_BUF(b2); + fprintf(f, "%s ", sprint_spi(&pinfo->spi, 0, b2)); + } else + fprintf(f, " "); + } else if (pinfo && pinfo->spi.mask) { + SPRINT_BUF(b2); + fprintf(f, "sender [NONE]%s ", sprint_spi(&pinfo->spi, 0, b2)); + } + if (tb[TCA_RSVP_POLICE]) + tc_print_police(f, tb[TCA_RSVP_POLICE]); + return 0; +} + +struct filter_util rsvp_filter_util = { + .id = "rsvp", + .parse_fopt = rsvp_parse_opt, + .print_fopt = rsvp_print_opt, +}; + +struct filter_util rsvp6_filter_util = { + .id = "rsvp6", + .parse_fopt = rsvp_parse_opt, + .print_fopt = rsvp_print_opt, +}; diff --git a/tc/f_rsvp.o b/tc/f_rsvp.o new file mode 100644 index 0000000000000000000000000000000000000000..7223badc5ad17bb01626f6017910c6a16e814f01 GIT binary patch literal 12128 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 literal 0 HcmV?d00001 diff --git a/tc/f_tcindex.c b/tc/f_tcindex.c new file mode 100644 index 0000000..39ac75a --- /dev/null +++ b/tc/f_tcindex.c @@ -0,0 +1,185 @@ +/* + * f_tcindex.c Traffic control index filter + * + * Written 1998,1999 by Werner Almesberger + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <string.h> +#include <netinet/in.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr," Usage: ... tcindex [ hash SIZE ] [ mask MASK ]" + " [ shift SHIFT ]\n"); + fprintf(stderr," [ pass_on | fall_through ]\n"); + fprintf(stderr," [ classid CLASSID ] " + "[ police POLICE_SPEC ]\n"); +} + + +#define usage() return(-1) + + +static int tcindex_parse_opt(struct filter_util *qu, char *handle, int argc, + char **argv, struct nlmsghdr *n) +{ + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + char *end; + + if (handle) { + t->tcm_handle = strtoul(handle,&end,0); + if (*end) { + fprintf(stderr, "Illegal filter ID\n"); + return -1; + } + } + if (!argc) return 0; + tail = NLMSG_TAIL(n); + addattr_l(n,4096,TCA_OPTIONS,NULL,0); + while (argc) { + if (!strcmp(*argv,"hash")) { + int hash; + + NEXT_ARG(); + hash = strtoul(*argv,&end,0); + if (*end || !hash || hash > 0x10000) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_HASH,&hash,sizeof(hash)); + } + else if (!strcmp(*argv,"mask")) { + __u16 mask; + + NEXT_ARG(); + mask = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_MASK,&mask,sizeof(mask)); + } + else if (!strcmp(*argv,"shift")) { + int shift; + + NEXT_ARG(); + shift = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + addattr_l(n,4096,TCA_TCINDEX_SHIFT,&shift, + sizeof(shift)); + } + else if (!strcmp(*argv,"fall_through")) { + int value = 1; + + addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + sizeof(value)); + } + else if (!strcmp(*argv,"pass_on")) { + int value = 0; + + addattr_l(n,4096,TCA_TCINDEX_FALL_THROUGH,&value, + sizeof(value)); + } + else if (!strcmp(*argv,"classid")) { + __u32 handle; + + NEXT_ARG(); + if (get_tc_classid(&handle,*argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, 4096, TCA_TCINDEX_CLASSID, &handle, 4); + } + else if (!strcmp(*argv,"police")) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_TCINDEX_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } + else { + explain(); + return -1; + } + argc--; + argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static int tcindex_print_opt(struct filter_util *qu, FILE *f, + struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_TCINDEX_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_TCINDEX_MAX, opt); + + if (handle != ~0) fprintf(f,"handle 0x%04x ",handle); + if (tb[TCA_TCINDEX_HASH]) { + __u16 hash; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_HASH]) < sizeof(hash)) + return -1; + hash = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_HASH]); + fprintf(f,"hash %d ",hash); + } + if (tb[TCA_TCINDEX_MASK]) { + __u16 mask; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_MASK]) < sizeof(mask)) + return -1; + mask = *(__u16 *) RTA_DATA(tb[TCA_TCINDEX_MASK]); + fprintf(f,"mask 0x%04x ",mask); + } + if (tb[TCA_TCINDEX_SHIFT]) { + int shift; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_SHIFT]) < sizeof(shift)) + return -1; + shift = *(int *) RTA_DATA(tb[TCA_TCINDEX_SHIFT]); + fprintf(f,"shift %d ",shift); + } + if (tb[TCA_TCINDEX_FALL_THROUGH]) { + int fall_through; + + if (RTA_PAYLOAD(tb[TCA_TCINDEX_FALL_THROUGH]) < + sizeof(fall_through)) + return -1; + fall_through = *(int *) RTA_DATA(tb[TCA_TCINDEX_FALL_THROUGH]); + fprintf(f,fall_through ? "fall_through " : "pass_on "); + } + if (tb[TCA_TCINDEX_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "classid %s ",sprint_tc_classid(*(__u32 *) + RTA_DATA(tb[TCA_TCINDEX_CLASSID]), b1)); + } + if (tb[TCA_TCINDEX_POLICE]) { + fprintf(f, "\n"); + tc_print_police(f, tb[TCA_TCINDEX_POLICE]); + } + return 0; +} + +struct filter_util tcindex_filter_util = { + .id = "tcindex", + .parse_fopt = tcindex_parse_opt, + .print_fopt = tcindex_print_opt, +}; diff --git a/tc/f_tcindex.o b/tc/f_tcindex.o new file mode 100644 index 0000000000000000000000000000000000000000..c76dd384c0ef040f21ca236706087e2f9c8d385c GIT binary patch literal 5432 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 literal 0 HcmV?d00001 diff --git a/tc/f_u32.c b/tc/f_u32.c new file mode 100644 index 0000000..50dc4df --- /dev/null +++ b/tc/f_u32.c @@ -0,0 +1,1071 @@ +/* + * q_u32.c U32 filter. + * + * This program is free software; you can u32istribute 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * Match mark added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro> [5 nov 2004] + * + */ + +#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 <linux/if.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]\n"); + fprintf(stderr, " [ police POLICE_SPEC ] [ offset OFFSET_SPEC ]\n"); + fprintf(stderr, " [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n"); + fprintf(stderr, " [ sample SAMPLE ]\n"); + fprintf(stderr, "or u32 divisor DIVISOR\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n"); + fprintf(stderr, " SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} | mark } SAMPLE_ARGS\n"); + fprintf(stderr, " FILTERID := X:Y:Z\n"); +} + +#define usage() return(-1) + +int get_u32_handle(__u32 *handle, char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +char * sprint_u32_handle(__u32 handle, char *buf) +{ + int bsize = SPRINT_BSIZE-1; + __u32 htid = TC_U32_HTID(handle); + __u32 hash = TC_U32_HASH(handle); + __u32 nodeid = TC_U32_NODE(handle); + char *b = buf; + + if (handle == 0) { + snprintf(b, bsize, "none"); + return b; + } + if (htid) { + int l = snprintf(b, bsize, "%x:", htid>>20); + bsize -= l; + b += l; + } + if (nodeid|hash) { + if (hash) { + int l = snprintf(b, bsize, "%x", hash); + bsize -= l; + b += l; + } + if (nodeid) { + int l = snprintf(b, bsize, ":%x", nodeid); + bsize -= l; + b += l; + } + } + if (show_raw) + snprintf(b, bsize, "[%08x] ", handle); + return buf; +} + +static int pack_key(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + int i; + int hwm = sel->nkeys; + + key &= mask; + + for (i=0; i<hwm; i++) { + if (sel->keys[i].off == off && sel->keys[i].offmask == offmask) { + __u32 intersect = mask&sel->keys[i].mask; + + if ((key^sel->keys[i].val) & intersect) + return -1; + sel->keys[i].val |= key; + sel->keys[i].mask |= mask; + return 0; + } + } + + if (hwm >= 128) + return -1; + if (off % 4) + return -1; + sel->keys[hwm].val = key; + sel->keys[hwm].mask = mask; + sel->keys[hwm].off = off; + sel->keys[hwm].offmask = offmask; + sel->nkeys++; + return 0; +} + +static int pack_key32(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + key = htonl(key); + mask = htonl(mask); + return pack_key(sel, key, mask, off, offmask); +} + +static int pack_key16(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + if (key > 0xFFFF || mask > 0xFFFF) + return -1; + + if ((off & 3) == 0) { + key <<= 16; + mask <<= 16; + } + off &= ~3; + key = htonl(key); + mask = htonl(mask); + + return pack_key(sel, key, mask, off, offmask); +} + +static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask) +{ + if (key > 0xFF || mask > 0xFF) + return -1; + + if ((off & 3) == 0) { + key <<= 24; + mask <<= 24; + } else if ((off & 3) == 1) { + key <<= 16; + mask <<= 16; + } else if ((off & 3) == 2) { + key <<= 8; + mask <<= 8; + } + off &= ~3; + key = htonl(key); + mask = htonl(mask); + + return pack_key(sel, key, mask, off, offmask); +} + + +int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask) +{ + int argc = *argc_p; + char **argv = *argv_p; + char *p = *argv; + + if (argc <= 0) + return -1; + + if (strlen(p) > strlen("nexthdr+") && + memcmp(p, "nexthdr+", strlen("nexthdr+")) == 0) { + *offmask = -1; + p += strlen("nexthdr+"); + } else if (matches(*argv, "nexthdr+") == 0) { + NEXT_ARG(); + *offmask = -1; + p = *argv; + } + + if (get_integer(off, p, 0)) + return -1; + argc--; argv++; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +static int parse_u32(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + res = pack_key32(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_u16(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + res = pack_key16(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_u8(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off, int offmask) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + __u32 key; + __u32 mask; + + if (argc < 2) + return -1; + + if (get_u32(&key, *argv, 0)) + return -1; + argc--; argv++; + + if (get_u32(&mask, *argv, 16)) + return -1; + argc--; argv++; + + if (key > 0xFF || mask > 0xFF) + return -1; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + res = pack_key8(sel, key, mask, off, offmask); + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + inet_prefix addr; + __u32 mask; + int offmask = 0; + + if (argc < 1) + return -1; + + if (get_prefix_1(&addr, *argv, AF_INET)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + mask = 0; + if (addr.bitlen) + mask = htonl(0xFFFFFFFF<<(32-addr.bitlen)); + if (pack_key(sel, addr.data[0], mask, off, offmask) < 0) + return -1; + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip6_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, int off) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + int plen = 128; + int i; + inet_prefix addr; + int offmask = 0; + + if (argc < 1) + return -1; + + if (get_prefix_1(&addr, *argv, AF_INET6)) + return -1; + argc--; argv++; + + if (argc > 0 && strcmp(argv[0], "at") == 0) { + NEXT_ARG(); + if (parse_at(&argc, &argv, &off, &offmask)) + return -1; + } + + plen = addr.bitlen; + for (i=0; i<plen; i+=32) { +// if (((i+31)&~0x1F)<=plen) { + if (((i+31))<=plen) { + if ((res = pack_key(sel, addr.data[i/32], 0xFFFFFFFF, off+4*(i/32), offmask)) < 0) + return -1; + } else if (i<plen) { + __u32 mask = htonl(0xFFFFFFFF<<(32-(plen-i))); + if ((res = pack_key(sel, addr.data[i/32], mask, off+4*(i/32), offmask)) < 0) + return -1; + } + } + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_ip_addr(&argc, &argv, sel, 12); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_ip_addr(&argc, &argv, sel, 16); + goto done; + } + if (strcmp(*argv, "tos") == 0 || + matches(*argv, "dsfield") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, 0); + goto done; + } + if (strcmp(*argv, "ihl") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 9, 0); + goto done; + } + if (matches(*argv, "precedence") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, 0); + goto done; + } + if (strcmp(*argv, "nofrag") == 0) { + argc--; argv++; + res = pack_key16(sel, 0, 0x3FFF, 6, 0); + goto done; + } + if (strcmp(*argv, "firstfrag") == 0) { + argc--; argv++; + res = pack_key16(sel, 0, 0x1FFF, 6, 0); + goto done; + } + if (strcmp(*argv, "df") == 0) { + argc--; argv++; + res = pack_key16(sel, 0x4000, 0x4000, 6, 0); + goto done; + } + if (strcmp(*argv, "mf") == 0) { + argc--; argv++; + res = pack_key16(sel, 0x2000, 0x2000, 6, 0); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 22, 0); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 20, 0); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 20, 0); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 20, 1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_ip6(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_ip6_addr(&argc, &argv, sel, 8); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_ip6_addr(&argc, &argv, sel, 24); + goto done; + } + if (strcmp(*argv, "priority") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 6, 0); + goto done; + } + if (strcmp(*argv, "flowlabel") == 0) { + NEXT_ARG(); + res = parse_u32(&argc, &argv, sel, 0, 0); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 42, 0); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 40, 0); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 40, 0); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 41, 1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +#define parse_tcp parse_udp +static int parse_udp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 0, -1); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 2, -1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_icmp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, -1); + goto done; + } + if (strcmp(*argv, "code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 1, -1); + goto done; + } + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_mark(int *argc_p, char ***argv_p, struct nlmsghdr *n) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + struct tc_u32_mark mark; + + if (argc <= 1) + return -1; + + if (get_u32(&mark.val, *argv, 0)) { + fprintf(stderr, "Illegal \"mark\" value\n"); + return -1; + } + NEXT_ARG(); + + if (get_u32(&mark.mask, *argv, 0)) { + fprintf(stderr, "Illegal \"mark\" mask\n"); + return -1; + } + NEXT_ARG(); + + if ((mark.val & mark.mask) != mark.val) { + fprintf(stderr, "Illegal \"mark\" (impossible combination)\n"); + return -1; + } + + addattr_l(n, MAX_MSG, TCA_U32_MARK, &mark, sizeof(mark)); + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_selector(int *argc_p, char ***argv_p, struct tc_u32_sel *sel, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + if (matches(*argv, "u32") == 0) { + NEXT_ARG(); + res = parse_u32(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "u16") == 0) { + NEXT_ARG(); + res = parse_u16(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "u8") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, sel, 0, 0); + goto done; + } + if (matches(*argv, "ip") == 0) { + NEXT_ARG(); + res = parse_ip(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "ip6") == 0) { + NEXT_ARG(); + res = parse_ip6(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "udp") == 0) { + NEXT_ARG(); + res = parse_udp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "tcp") == 0) { + NEXT_ARG(); + res = parse_tcp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "icmp") == 0) { + NEXT_ARG(); + res = parse_icmp(&argc, &argv, sel); + goto done; + } + if (matches(*argv, "mark") == 0) { + NEXT_ARG(); + res = parse_mark(&argc, &argv, n); + goto done; + } + + return -1; + +done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int parse_offset(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int argc = *argc_p; + char **argv = *argv_p; + + while (argc > 0) { + if (matches(*argv, "plus") == 0) { + int off; + NEXT_ARG(); + if (get_integer(&off, *argv, 0)) + return -1; + sel->off = off; + sel->flags |= TC_U32_OFFSET; + } else if (matches(*argv, "at") == 0) { + int off; + NEXT_ARG(); + if (get_integer(&off, *argv, 0)) + return -1; + sel->offoff = off; + if (off%2) { + fprintf(stderr, "offset \"at\" must be even\n"); + return -1; + } + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "mask") == 0) { + __u16 mask; + NEXT_ARG(); + if (get_u16(&mask, *argv, 16)) + return -1; + sel->offmask = htons(mask); + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "shift") == 0) { + int shift; + NEXT_ARG(); + if (get_integer(&shift, *argv, 0)) + return -1; + sel->offshift = shift; + sel->flags |= TC_U32_VAROFFSET; + } else if (matches(*argv, "eat") == 0) { + sel->flags |= TC_U32_EAT; + } else { + break; + } + argc--; argv++; + } + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int parse_hashkey(int *argc_p, char ***argv_p, struct tc_u32_sel *sel) +{ + int argc = *argc_p; + char **argv = *argv_p; + + while (argc > 0) { + if (matches(*argv, "mask") == 0) { + __u32 mask; + NEXT_ARG(); + if (get_u32(&mask, *argv, 16)) + return -1; + sel->hmask = htonl(mask); + } else if (matches(*argv, "at") == 0) { + int num; + NEXT_ARG(); + if (get_integer(&num, *argv, 0)) + return -1; + if (num%4) + return -1; + sel->hoff = num; + } else { + break; + } + argc--; argv++; + } + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +static int u32_parse_opt(struct filter_util *qu, char *handle, int argc, char **argv, struct nlmsghdr *n) +{ + struct { + struct tc_u32_sel sel; + struct tc_u32_key keys[128]; + } sel; + struct tcmsg *t = NLMSG_DATA(n); + struct rtattr *tail; + int sel_ok = 0; + int sample_ok = 0; + __u32 htid = 0; + __u32 order = 0; + + memset(&sel, 0, sizeof(sel)); + + if (handle && get_u32_handle(&t->tcm_handle, handle)) { + fprintf(stderr, "Illegal filter ID\n"); + return -1; + } + + if (argc == 0) + return 0; + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0); + + while (argc > 0) { + if (matches(*argv, "match") == 0) { + NEXT_ARG(); + if (parse_selector(&argc, &argv, &sel.sel, n)) { + fprintf(stderr, "Illegal \"match\"\n"); + return -1; + } + sel_ok++; + continue; + } else if (matches(*argv, "offset") == 0) { + NEXT_ARG(); + if (parse_offset(&argc, &argv, &sel.sel)) { + fprintf(stderr, "Illegal \"offset\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "hashkey") == 0) { + NEXT_ARG(); + if (parse_hashkey(&argc, &argv, &sel.sel)) { + fprintf(stderr, "Illegal \"hashkey\"\n"); + return -1; + } + continue; + } else if (matches(*argv, "classid") == 0 || + strcmp(*argv, "flowid") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) { + fprintf(stderr, "Illegal \"classid\"\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4); + sel.sel.flags |= TC_U32_TERMINAL; + } else if (matches(*argv, "divisor") == 0) { + unsigned divisor; + NEXT_ARG(); + if (get_unsigned(&divisor, *argv, 0) || divisor == 0 || + divisor > 0x100) { + fprintf(stderr, "Illegal \"divisor\"\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_DIVISOR, &divisor, 4); + } else if (matches(*argv, "order") == 0) { + NEXT_ARG(); + if (get_u32(&order, *argv, 0)) { + fprintf(stderr, "Illegal \"order\"\n"); + return -1; + } + } else if (strcmp(*argv, "link") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_u32_handle(&handle, *argv)) { + fprintf(stderr, "Illegal \"link\"\n"); + return -1; + } + if (handle && TC_U32_NODE(handle)) { + fprintf(stderr, "\"link\" must be a hash table.\n"); + return -1; + } + addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4); + } else if (strcmp(*argv, "ht") == 0) { + unsigned handle; + NEXT_ARG(); + if (get_u32_handle(&handle, *argv)) { + fprintf(stderr, "Illegal \"ht\"\n"); + return -1; + } + if (handle && TC_U32_NODE(handle)) { + fprintf(stderr, "\"ht\" must be a hash table.\n"); + return -1; + } + if (sample_ok) + htid = (htid&0xFF000)|(handle&0xFFF00000); + else + htid = (handle&0xFFFFF000); + } else if (strcmp(*argv, "sample") == 0) { + __u32 hash; + struct { + struct tc_u32_sel sel; + struct tc_u32_key keys[4]; + } sel2; + NEXT_ARG(); + if (parse_selector(&argc, &argv, &sel2.sel, n)) { + fprintf(stderr, "Illegal \"sample\"\n"); + return -1; + } + if (sel2.sel.nkeys != 1) { + fprintf(stderr, "\"sample\" must contain exactly ONE key.\n"); + return -1; + } + hash = sel2.sel.keys[0].val&sel2.sel.keys[0].mask; + hash ^= hash>>16; + hash ^= hash>>8; + htid = ((hash<<12)&0xFF000)|(htid&0xFFF00000); + sample_ok = 1; + continue; + } else if (strcmp(*argv, "indev") == 0) { + char ind[IFNAMSIZ + 1]; + memset(ind, 0, sizeof (ind)); + argc--; + argv++; + if (argc < 1) { + fprintf(stderr, "Illegal indev\n"); + return -1; + } + strncpy(ind, *argv, sizeof (ind) - 1); + addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, strlen(ind) + 1); + + } else if (matches(*argv, "action") == 0) { + NEXT_ARG(); + if (parse_action(&argc, &argv, TCA_U32_ACT, n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + continue; + + } else if (matches(*argv, "police") == 0) { + NEXT_ARG(); + if (parse_police(&argc, &argv, TCA_U32_POLICE, n)) { + fprintf(stderr, "Illegal \"police\"\n"); + return -1; + } + continue; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (order) { + if (TC_U32_NODE(t->tcm_handle) && order != TC_U32_NODE(t->tcm_handle)) { + fprintf(stderr, "\"order\" contradicts \"handle\"\n"); + return -1; + } + t->tcm_handle |= order; + } + + if (htid) + addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4); + if (sel_ok) + addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 handle) +{ + struct rtattr *tb[TCA_U32_MAX+1]; + struct tc_u32_sel *sel = NULL; + struct tc_u32_pcnt *pf = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_U32_MAX, opt); + + if (handle) { + SPRINT_BUF(b1); + fprintf(f, "fh %s ", sprint_u32_handle(handle, b1)); + } + if (TC_U32_NODE(handle)) { + fprintf(f, "order %d ", TC_U32_NODE(handle)); + } + + if (tb[TCA_U32_SEL]) { + if (RTA_PAYLOAD(tb[TCA_U32_SEL]) < sizeof(*sel)) + return -1; + + sel = RTA_DATA(tb[TCA_U32_SEL]); + } + + if (tb[TCA_U32_DIVISOR]) { + fprintf(f, "ht divisor %d ", *(__u32*)RTA_DATA(tb[TCA_U32_DIVISOR])); + } else if (tb[TCA_U32_HASH]) { + __u32 htid = *(__u32*)RTA_DATA(tb[TCA_U32_HASH]); + fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid), TC_U32_HASH(htid)); + } else { + fprintf(f, "??? "); + } + if (tb[TCA_U32_CLASSID]) { + SPRINT_BUF(b1); + fprintf(f, "%sflowid %s ", + !sel || !(sel->flags&TC_U32_TERMINAL) ? "*" : "", + sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_U32_CLASSID]), b1)); + } else if (sel && sel->flags&TC_U32_TERMINAL) { + fprintf(f, "terminal flowid ??? "); + } + if (tb[TCA_U32_LINK]) { + SPRINT_BUF(b1); + fprintf(f, "link %s ", sprint_u32_handle(*(__u32*)RTA_DATA(tb[TCA_U32_LINK]), b1)); + } + + if (tb[TCA_U32_PCNT]) { + if (RTA_PAYLOAD(tb[TCA_U32_PCNT]) < sizeof(*pf)) { + fprintf(f, "Broken perf counters \n"); + return -1; + } + pf = RTA_DATA(tb[TCA_U32_PCNT]); + } + + if (sel && show_stats && NULL != pf) + fprintf(f, " (rule hit %llu success %llu)", + (unsigned long long) pf->rcnt, + (unsigned long long) pf->rhit); + + if (tb[TCA_U32_MARK]) { + struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]); + if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) { + fprintf(f, "\n Invalid mark (kernel&iproute2 mismatch)\n"); + } else { + fprintf(f, "\n mark 0x%04x 0x%04x (success %d)", + mark->val, mark->mask, mark->success); + } + } + + if (sel) { + int i; + struct tc_u32_key *key = sel->keys; + if (sel->nkeys) { + for (i=0; i<sel->nkeys; i++, key++) { + fprintf(f, "\n match %08x/%08x at %s%d", + (unsigned int)ntohl(key->val), + (unsigned int)ntohl(key->mask), + key->offmask ? "nexthdr+" : "", + key->off); + if (show_stats && NULL != pf) + fprintf(f, " (success %lld ) ", + (unsigned long long) pf->kcnts[i]); + } + } + + if (sel->flags&(TC_U32_VAROFFSET|TC_U32_OFFSET)) { + fprintf(f, "\n offset "); + if (sel->flags&TC_U32_VAROFFSET) + fprintf(f, "%04x>>%d at %d ", ntohs(sel->offmask), sel->offshift, sel->offoff); + if (sel->off) + fprintf(f, "plus %d ", sel->off); + } + if (sel->flags&TC_U32_EAT) + fprintf(f, " eat "); + + if (sel->hmask) { + fprintf(f, "\n hash mask %08x at %d ", + (unsigned int)htonl(sel->hmask), sel->hoff); + } + } + + if (tb[TCA_U32_POLICE]) { + fprintf(f, "\n"); + tc_print_police(f, tb[TCA_U32_POLICE]); + } + if (tb[TCA_U32_INDEV]) { + struct rtattr *idev = tb[TCA_U32_INDEV]; + fprintf(f, "\n input dev %s\n", (char *) RTA_DATA(idev)); + } + if (tb[TCA_U32_ACT]) { + tc_print_action(f, tb[TCA_U32_ACT]); + } + + return 0; +} + +struct filter_util u32_filter_util = { + .id = "u32", + .parse_fopt = u32_parse_opt, + .print_fopt = u32_print_opt, +}; diff --git a/tc/f_u32.o b/tc/f_u32.o new file mode 100644 index 0000000000000000000000000000000000000000..25f9e21939bad118a0ecdd1f452f521b25820f11 GIT binary patch literal 23608 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 ZF)sz)!_RX?eIe+=&-e0qEa<||{}=NeM<oCN literal 0 HcmV?d00001 diff --git a/tc/libtc.a b/tc/libtc.a new file mode 100644 index 0000000000000000000000000000000000000000..fc0f818b11f27d816d058c8e17daf8b43c4812f0 GIT binary patch literal 11328 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 literal 0 HcmV?d00001 diff --git a/tc/m_action.c b/tc/m_action.c new file mode 100644 index 0000000..2d2b0ed --- /dev/null +++ b/tc/m_action.c @@ -0,0 +1,608 @@ +/* + * m_action.c Action Management + * + * 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 (hadi@cyberus.ca) + * + * TODO: + * - parse to be passed a filedescriptor for logging purposes + * +*/ + +#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 <dlfcn.h> + +#include "utils.h" +#include "tc_common.h" +#include "tc_util.h" + +static struct action_util * action_list; +#ifdef CONFIG_GACT +int gact_ld = 0 ; //fuckin backward compatibility +#endif +int batch_c = 0; +int tab_flush = 0; + +void act_usage(void) +{ + fprintf (stderr, "action usage improper\n"); +} + +static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "[Unknown action, optlen=%u] ", + (unsigned) RTA_PAYLOAD(opt)); + return 0; +} + +static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc) { + fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv); + } else { + fprintf(stderr, "Unknown action \"%s\"\n", au->id); + } + return -1; +} + +struct action_util *get_action_kind(char *str) +{ + static void *aBODY; + void *dlh; + char buf[256]; + struct action_util *a; +#ifdef CONFIG_GACT + int looked4gact = 0; +restart_s: +#endif + for (a = action_list; a; a = a->next) { + if (strcmp(a->id, str) == 0) + return a; + } + + snprintf(buf, sizeof(buf), "m_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = aBODY; + if (dlh == NULL) { + dlh = aBODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_action_util", str); + a = dlsym(dlh, buf); + if (a == NULL) + goto noexist; + +reg: + a->next = action_list; + action_list = a; + return a; + +noexist: +#ifdef CONFIG_GACT + if (!looked4gact) { + looked4gact = 1; + strcpy(str,"gact"); + goto restart_s; + } +#endif + a = malloc(sizeof(*a)); + if (a) { + memset(a, 0, sizeof(*a)); + strncpy(a->id, "noact", 15); + a->parse_aopt = parse_noaopt; + a->print_aopt = print_noaopt; + goto reg; + } + return a; +} + +int +new_cmd(char **argv) +{ + if ((matches(*argv, "change") == 0) || + (matches(*argv, "replace") == 0)|| + (matches(*argv, "delete") == 0)|| + (matches(*argv, "add") == 0)) + return 1; + + return 0; + +} + +int +parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + struct rtattr *tail, *tail2; + char k[16]; + int ok = 0; + int eap = 0; /* expect action parameters */ + + int ret = 0; + int prio = 0; + + if (argc <= 0) + return -1; + + tail = tail2 = NLMSG_TAIL(n); + + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + + while (argc > 0) { + + memset(k, 0, sizeof (k)); + + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + eap = 1; +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } else if (new_cmd(argv)) { + goto done0; + } else { + struct action_util *a = NULL; + strncpy(k, *argv, sizeof (k) - 1); + eap = 0; + if (argc > 0 ) { + a = get_action_kind(k); + } else { +done0: + if (ok) + break; + else + goto done; + } + + if (NULL == a) { + goto bad_val; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, ++prio, NULL, 0); + addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + + ret = a->parse_aopt(a,&argc, &argv, TCA_ACT_OPTIONS, n); + + if (ret < 0) { + fprintf(stderr,"bad action parsing\n"); + goto bad_val; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + ok++; + } + + } + + if (eap > 0) { + fprintf(stderr,"bad action empty %d\n",eap); + goto bad_val; + } + + tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2; + +done: + *argc_p = argc; + *argv_p = argv; + return 0; +bad_val: + /* no need to undo things, returning from here should + * cause enough pain */ + fprintf(stderr, "parse_action: bad value (%d:%s)!\n",argc,*argv); + return -1; +} + +int +tc_print_one_action(FILE * f, struct rtattr *arg) +{ + + struct rtattr *tb[TCA_ACT_MAX + 1]; + int err = 0; + struct action_util *a = NULL; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_ACT_MAX, arg); + if (tb[TCA_ACT_KIND] == NULL) { + fprintf(stderr, "NULL Action!\n"); + return -1; + } + + + a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND])); + if (NULL == a) + return err; + + if (tab_flush) { + fprintf(f," %s \n", a->id); + tab_flush = 0; + return 0; + } + + err = a->print_aopt(a,f,tb[TCA_ACT_OPTIONS]); + + + if (0 > err) + return err; + + if (show_stats && tb[TCA_ACT_STATS]) { + fprintf(f, "\tAction statistics:\n"); + print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL); + fprintf(f, "\n"); + } + + return 0; +} + +int +tc_print_action(FILE * f, const struct rtattr *arg) +{ + + int i; + struct rtattr *tb[TCA_ACT_MAX_PRIO + 1]; + + if (arg == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg); + + if (tab_flush && NULL != tb[0] && NULL == tb[1]) { + int ret = tc_print_one_action(f, tb[0]); + return ret; + } + + for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + if (tb[i]) { + fprintf(f, "\n\taction order %d: ", i + batch_c); + if (0 > tc_print_one_action(f, tb[i])) { + fprintf(f, "Error printing action\n"); + } + } + + } + + batch_c+=TCA_ACT_MAX_PRIO ; + return 0; +} + +static int do_print_action(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcamsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCAA_MAX+1]; + + len -= NLMSG_LENGTH(sizeof(*t)); + + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); + + if (NULL == tb[TCA_ACT_TAB]) { + if (n->nlmsg_type != RTM_GETACTION) + fprintf(stderr, "do_print_action: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELACTION) { + if (n->nlmsg_flags & NLM_F_ROOT) { + fprintf(fp, "Flushed table "); + tab_flush = 1; + } else { + fprintf(fp, "deleted action "); + } + } + + if (n->nlmsg_type == RTM_NEWACTION) + fprintf(fp, "Added action "); + tc_print_action(fp, tb[TCA_ACT_TAB]); + + return 0; +} + +int tc_action_gd(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + char k[16]; + struct action_util *a = NULL; + int argc = *argc_p; + char **argv = *argv_p; + int prio = 0; + int ret = 0; + __u32 i; + struct sockaddr_nl nladdr; + struct rtattr *tail; + struct rtattr *tail2; + struct nlmsghdr *ans = NULL; + + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + argc -=1; + argv +=1; + + + tail = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + + while (argc > 0) { + if (strcmp(*argv, "action") == 0 ) { + argc--; + argv++; + continue; + } else if (strcmp(*argv, "help") == 0) { + return -1; + } + + strncpy(k, *argv, sizeof (k) - 1); + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr, "Error: non existent action: %s\n",k); + ret = -1; + goto bad_val; + } + + argc -=1; + argv +=1; + if (argc <= 0) { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&i, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + ret = -1; + goto bad_val; + } + argc -=1; + argv +=1; + } else { + fprintf(stderr, "Error: no index specified action: %s\n",k); + ret = -1; + goto bad_val; + } + + tail2 = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); + tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; + + } + + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + req.n.nlmsg_seq = rth.dump = ++rth.seq; + if (cmd == RTM_GETACTION) + ans = &req.n; + + if (rtnl_talk(&rth, &req.n, 0, 0, ans, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + return 1; + } + + if (ans && do_print_action(NULL, &req.n, (void*)stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + *argc_p = argc; + *argv_p = argv; +bad_val: + return ret; +} + +int tc_action_modify(int cmd, unsigned flags, int *argc_p, char ***argv_p) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ret = 0; + + struct rtattr *tail; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + tail = NLMSG_TAIL(&req.n); + argc -=1; + argv +=1; + if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + ret = -1; + } + + *argc_p = argc; + *argv_p = argv; + + return ret; +} + +int tc_act_list_or_flush(int argc, char **argv, int event) +{ + int ret = 0, prio = 0, msg_size = 0; + char k[16]; + struct rtattr *tail,*tail2; + struct action_util *a = NULL; + struct { + struct nlmsghdr n; + struct tcamsg t; + char buf[MAX_MSG]; + } req; + + req.t.tca_family = AF_UNSPEC; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)); + + tail = NLMSG_TAIL(&req.n); + addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0); + tail2 = NLMSG_TAIL(&req.n); + + strncpy(k, *argv, sizeof (k) - 1); +#ifdef CONFIG_GACT + if (!gact_ld) { + get_action_kind("gact"); + } +#endif + a = get_action_kind(k); + if (NULL == a) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + if (strcmp(a->id, k) != 0) { + fprintf(stderr,"bad action %s\n",k); + goto bad_val; + } + strncpy(k, *argv, sizeof (k) - 1); + + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); + addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); + tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; + tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + + msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + + if (event == RTM_GETACTION) { + if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) { + perror("Cannot send dump request"); + return 1; + } + ret = rtnl_dump_filter(&rth, do_print_action, stdout, NULL, NULL); + } + + if (event == RTM_DELACTION) { + req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); + req.n.nlmsg_type = RTM_DELACTION; + req.n.nlmsg_flags |= NLM_F_ROOT; + req.n.nlmsg_flags |= NLM_F_REQUEST; + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error flushing\n"); + return 1; + } + + } + +bad_val: + + return ret; +} + +int do_action(int argc, char **argv) +{ + + int ret = 0; + + while (argc > 0) { + + if (matches(*argv, "add") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv); + } else if (matches(*argv, "change") == 0 || + matches(*argv, "replace") == 0) { + ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv); + } else if (matches(*argv, "delete") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv); + } else if (matches(*argv, "get") == 0) { + argc -=1; + argv +=1; + ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv); + } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION); + } else if (matches(*argv, "flush") == 0) { + if (argc <= 2) { + act_usage(); + return -1; + } + return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION); + } else if (matches(*argv, "help") == 0) { + act_usage(); + return -1; + } else { + + ret = -1; + } + + if (ret < 0) { + fprintf(stderr, "Command \"%s\" is unknown, try \"tc action help\".\n", *argv); + return -1; + } + } + + return 0; +} + diff --git a/tc/m_action.o b/tc/m_action.o new file mode 100644 index 0000000000000000000000000000000000000000..c09dc34ba869d098e02e7d0c40f95ff287fc4329 GIT binary patch literal 14408 zcmbtae{@tumaZfX#E9KdqZ8ECCpHpR$fki+26ZMa9e81z5l4PFqJ}o<4motXWA}@M z9mXNsY1X%GL(b05InJoN%#Q1E&p5OGnh`i22tR{*Mp+z36i3jbD={j#xaca<``vm~ z>6=G-{9{YI`_)%peRb<r)vc;We%utEUtU%wbSe|y5yhNFg)k-+pBv?4qwtDzg_%2# za@#J(*LJG7gR(>P6^r%9`pwKom1g#!X@6>6?t890mifYCUF3W2z;<Go_A8kWJ%NE^ zlnD%&nLU-eV^nSA<J8ZU=Pu43wESj%d8L`d|2I+vDWNKFDIm%E4(HDhm^hjVE^P{Y z99**0w7<U9?ER<`T`+ThLGr_w64T88u+p?YkrrkLe0_gHiJ809%pUH3&$NfO5pz3v z%(OoU3^d#CNv8A!5ScNv1E#&(*Y^xn<yFE|#i7_o!uEeLc6WPI_T%mggTE~sY!dB} zR%>6AM-1JKPVQ)cjH<L7KFc1qZa%$rywpB&()^XVHIW1#`N4VB_!hCjc{SAV-&F3K zcd;-B=hYI5Uqk-p#>pSM!n9?#P5Y49yNCR+;?Ce5!8>;=cc39(_C^>GD9GLwja34> zec8)VJTwnqj*l^p{kNL-S7z?Ja;#*P>_GRq+i0S!un+8pe{<LKGV47z@aiZaa1izP z%KF3Gsg>aJra(cCesEb+;8bua#u9D5gn4I%audS2i%ol-zt*%ZfAwI<@1f2%Wd}lb z$nOu^OZ;`dji17rTv=0KV7q7}M;*xwRAi2xHp})0UNFI{ly--5sDYZkjhny^=i~l{ z;hC}ikTd^-h5gN$U6n=I;123zQ}zX4-)20?ii@aX4sDvSgoaUUnSd|K&7quJI;Oqz z^rBo<*nausW7aRWkaHyt;55okKIZHDIqBH1D|h;`uOV;3yt=S`e47sy;1V;}Aj@;> z{QhjgxA7@i=eWe=gxu@vn*}Ua5iINrynx<^?EfC^fN<VlEBRr2C|71?c6##tg)?W) ze171{UA^zFN3xFDSjdgiX*-Wqf4*z|dLewF17<#0Zu&y|#X&hzX6B8G@U)>&K3E1R zDcNiv+FA}v%{;jyZ~6VEeY~)=7XH1z7kI9sfWeYJ*Q0SL7MgZ67PLKb6gE9`rch`? z&o*tAbQ6B9EK;w*u*K|h8Z$E&#`vSS+Afn`GVO91YfNW?2k#2D?3ON;mdQ4UAJ%d0 zLZ}zxn&UolHT3*W40D1J!jfLgnLVbQkW5}htdRXB4_eZi(n7<(%~^g=_LMJML+h%6 z)>Z$l+wfDMMV0xe!OWhr629lWW`3Cm(Plf%?#z&9#7ab7gsk5HHS;|V95@Kf`gX{P zaZLzge=U7u+HafoU}3S59k6%xObX>LLM=?&gI1-PS?6yQ-SvTk6c00d8telrcANRd zl}`WdBRYT|9F(DeDh|JH+HY(_+&SBMmcLO(O<G(>98>MLcT|BYGl7G{4{7Jq*5Y!$ z?EcWb<O|v-`h!a->Ym1$@BOHn)&cz0_xGsB++rZB-Qx5c-A)BQ9zG}5U)Y=Z`idVf zHe3H>W^Q;(^jn|UA6Ce|%iggNotym@a?-}^z}msE{f=wXE9{kp(7W%+jP!(odDgp! z%%Zt~NIKh3LzX3vRtjHsExMsKhhL)(^<yJ?4e{$FhPO87%?#D}?td5NV}xsHgdwGZ zza!E2GXTEl=6SHO2CT{WZm<@am^aw8oW(AudhLokv9XfBPR*N8W-X*`$cvgT=aP^! zhSUy;bw5MYF~(u|=Z+o~Q^*-Z2?09uL(fd>QazlPp3RdBW$1RA(#|@vU*s#qDZXBg zwsS0y@jkeW;(XBlEO1H={FX*Uj7!Yiv@jw`BjWuW6QR9fTMcm24*Tm6Vf|<vQS)<( zwsK$G7xFJwzQlsAHuKN>SAxHlx_%m~at>l*;U(;-A^WHda<Rca$*2~Y{beyU5R%a? zhAm=#bAEmegd1tbo0+}ve#l=tNYS0zbSBMV-^L8Y!}&-3n;Wr2E};FZfSqb@CHB^h z?NpX)5?iU3j7O)#_C9O^Z=II8tu&=1;_Lel#7^mU<n;v7zjDn_1_5inQlBai2eMvZ z#pV_gBljcc6s2ahFFSh_Cn-7IP+I4|VcW<OC9&&J1h+K?3xjI+Kxjj+&|9YsCl)Wt zPaiMEv0P}v^nb%fg6iM6k-Xz<B=1OF1os=ZRlqa4bq9rIjseYi&v<Mc$Fhg9bu<az zrk`QoAj2u@)zVmW!Ro*%c#l=M2BU}VL%ZevU!Z|ppZ@{bLE@T&9yy^316YT1X4*~} ze)>Vd*qapFtDAkHeNBNE@ZZ5^dw}Ld^@!wVNKzcA7S<%wiM9a*gHy-8{Hjo(ZBQN; zVPu2a+CQ)_G3`3tJwE~qP5V)s%veWpSz!xJ>a%Nv?RT9R?yNl($7!)(#TAAh)C+ei zYP%M=o^4Wj_))Di{H(4+Yphsj_(f+a&zCFRv2!#-+Buw&KhE({#w*jl!+*fE<NibT zS7E!&e>9BeV_5oOoaj!3vDUvp<bNEV$d#Kolo^3j&Gu_@<u}_eBM9Ow1i#mqxiT#H z2806)t!xC#nhqYdG`VZAqA8M*n__6uUkIa0{>~aR19~MqV$jN_rGB-nOiO?+IT&TM zsLmbv&^QsBcF4>>>~DlY#GbA9xa>fWg{gp;LbCxk3KZx`q!fF)p@t}bgeEEtdTq1q zES`}4g_(QU-vd!I_k@2ve#5y({rAh?C*+(9=brUHDD%(D=z_r}r(n<d^OIuuG0hk~ z$iW>n1*By|Q8sAj3qGu}+!@&wAYp|Emj~_FrTd*A-8>Sc$yzx=@aAPu5zctPS+oK% zQsI1ejTDqll4GF`m&f%0$YSz&GqzG00O2mj`#6!0bHcfEqrnLEeGB8M2SA3!|0Mun z5vLs~MZ-v!UoIyL<VcM&LBcrHd_w?3KfWP=nbY2&00t>NQAT<dI5pB8lO~hB3!&=- zJ|7}@Efx53<^zR?b6_@k!s&f5Q<#}IPgu+G5b*Wg5BKVhgLD~Y1G%3Ci{gQI=uJdK zwfma(n`-+VW!vyGW`1e)_L)qTOdt&vy5x{Ku|yr}F!?VV>cOON&-z$z*nf3d8On_q z#?>Fd1;`1e(hm@}gKWZGf<}sQM2&)TZj`ZS$YA8rZy_YvA$@grABQHPV$lr_?^W=e z=tGAk{7>hwYyn|rqP#*5Pc`#lIxH;lPuwnW>n>i$$s=1~Ec`t&J8<7JxdW*1I!ATQ z+BqhA@mOg0rMxL@{=+|kJ<f3s%Nl!$TfB8Fv^!ap37pzNV~F53kpf$_bF(FnFOW_I z#NVx$xoWi~$R&=Wf9>vK-BGdu$jUwrKQ*Qib>x-I7v;W<4}wVt)arcCnQd}LW%fC{ zqSPO&wzX?a^og<1?kiPG_stw>g^V+GE3~Vf?K@xg3`X9WFmOd-$cHaf6L^)<kxUrf z=}3Fj=;-W9CA*@jDzRc|Vs#?9CSf?`(~V@86^|xvoYH-#Ax4)Olc%I7SBcJ+De3xj zQcOv=ICWaOt&X^8T@^{RM@1^y6_2z=MO!o;wW1=@)}~q(tD^BP`36Yb9cfd|8C{W7 zx+BqE#j$8-m$lZI(pDvmDQTlhoD+0RPg@bIBW-oGrso0_-&-0E8&1)cRif$~#~34- zYKx{&d#)jxQmJIh=t^}YEVQATtP=C%-RV`)Hp7bC9gi9!*w%)Os;%s1`DBX}P<@?i z$Yxh}Bw*h1R1&iAmNXVnv`5T~BoavrUId!55>YGOkyu?NLfxHRh80b9b|fNJ6m^^9 z@o0M_ZcLWd;MNhqu>=Tn)08w?iyEsU_ePCK!idU_#i$?Bd8*NLG|^_XQA?@lJ>Ai? zCEBBwh{I<hy(+mz#PJ|2(R>(VY^NKmqKVchO#zxJG~7l<+UQQuD3P<OP>-IYBi<kz z?~TN}qedNEJ|#U3L*XRp{JBN~LeU<W5KUOh$#XHRV|2%g#NpO<!%7;~s;IF#no30D zRn8!ijv+l|q`RW69kGs)A#>D(n3wGAjG+HTtL40&Zdj?c#$>Bik1$Qc$@Nu&jzN^l zL|ISu#0i!C*oG((E_ZdA6N2t~ln9$GpPVmRi<ClMc?px{OB_gke_uW8^6AK1tbCbj z?<Q|`zP!m>8}u3*JP%Zaymbq_PnKuKQG1Uv{vGN$=&jD+$|7#SXAdj?gg_+dZ7N^r zt=(AB<TY;c)&;!{LGPT*gbm{#7?<}f@UB<wlD<bhQ@twnEcL_QMzYt>%3mT967(M^ zpD*o|hI%&8r3GAkmN0&8QEx+e$XlDKSm12{hwNU*c<(#a9lPO&o21ob>&A-t()I=3 z6TM~B0n`+N?0TNn|FwEvxM6%|LOAbvVB8Y#lf7k|$~QW~)ZRyozuV=fvDzD8?UA3R zt2%fTr<GR7Yfx5*1@q?3HR=|8Z|O8+R{iYyna0e9hS`CJz;#C5_c2S&h~?ljuC0%? z&YC7feR^#tVh?_;l=HhvKc%AaNWB&9u|$1a#EOXeyVGe=pGs1O=z;pc=<FP1sZMlN zODq-XjEZ_hpJ+X-nSpTGx?0q?VqIYEIkx^kz75o+t~;Yp6lzqDZ(UHt=*d|XMU2*w zr<fgidi*NqaTUFwM4E^XwNptMB>9pbzgOc!xSB3_a?)k0p3duEcUv@XNYh>4Q1uO6 zQT^)nHB9TB+NYhKCtd$MHb<zOxRms7qk4ANZ;<ck7!i+@bo;tK#TI&YH6t46sEA~` z3Uqy^REV4L;PwfXRbS^Y=BS3q|McQU*Vq2P3z)XWso{M0XaWbv;3mevsEV$y<0)a3 z+-;o?%sqC*`*+p;8p^Qbu1|TFizIt9EpVi2^~#rSne-me-T!ldjn)2>SE2yvl&e8k z)*+4YO+1hIRG!h?#T93H!C_R4X$jdk)(8`>I>#EIaW03xp)l_MinCU^5cJQc#9yiS zdVh1)rDtTlYroZlR|b3Z^w9WRrL&kO8Is;7=($49JbD_PDsbEzD^&=6+9;MT#N+wQ z585NNehAlj{d<SPwS5|I1AYPXzdi@hJa)%6K%%<@zODq`2b}Z=@JmUb2mT4qW9dIs z0)Mmw{;LxBQzh^%z{ws%EfVqzr2%}#vS&YV;;&bHeg1f@1pi10`~#)arKYGphkR9n zPye7hmOU4iz^9hLX8@P>k4HmD`h0Vv!n@Rj(C3qdC3J2nf&ZWc-dY0hEP<~nf!|jG zw@cs;1E>D3q=k>9&rS48c(vET_4#O537tQbz~3){)7gvkkE%h_=O^4FotsX5t8i{u z_~(AyJk$k4Wg<7-9Qr|hgK(%V@-EXh`bs8nhH7!HHFrm>)>SR7@|M{Wmp9KAdEpgl z3%A0Qh^<L=;8q%w4aVqtr0=kFqXR3|+Sw)2302+}r+b`ei{r#5IwSFTvQ;FaYg$@6 z+mJ<D(e$YM5M6g8mX&IWlT@O$Ypt+a)%`J<(6=U41!q4{<;|>xuC|b(n`(>IDswYi zWO1yk+iESUIDVkK@<=-@iFT%O&!f(C&zh-iz!UDD@fO(w`9v2iazwJ*a*A5x$#hh7 zBydFRqT6~4ofUD1V)C8IwvL$W1=^GDZ%L+<x1}guH(T^|vxvkx;<)V!ykfu=TwuhM z;l(|a!Ui3^PSaZoBz=6Oh|YJbZ#&acMtZ`(rSkLDFX_+`O6%xzfLov9FeTDgTaV0V z<hOG+;5rX7oZI<`!nK{VRi5H7CAU3)bYO{l@LZ$v#~4n1()MGi$fWg~74HItlRoXK zntuhuX^+-8{Y?ZVt#gZ$6*n<H*IC4HdR3`)DE?5Qc4-gP__GQ>SCy^AFC~39)3_l~ zyufEV+z-Dmfm1x8MC}q^g)Q+V!+C!_L_~7)>lCj2uu`&wn8En;_d>c~As4@0@taHV zm%8{##lO7-Kjz}!tN5!+@ZV?n70k{Gbr_{$IvZ>I7b#r#_ivSc)P?sdd>zxdjOjeg zaD(AbGMug+y4|A;=XTDb!9yZDIlf=v+RhD<CB*L;pX(oHIA4z+x^!|%=Q!ijn$vcE z&G-}t<|w{L1s$@J>w6i_<HIzDbACYKx?lgI^ye@>*O|}w+@3{@&-Irxoa_9Q;giv( z?(Y)}=l<NraJuhk{+kTHis4@?T=(l))vpQa{6v1^_V^f1`d>I<zA#ncT4$Tmna=op zysmZeUs3!<#`go$_Ag@i6o%i%aPH4`m;Qe${W#-ud!Ar8_w&;X=l=X7!|4d5?L4e- z?S~_(-S-)v`}0$VU&(YPsLf34oK!kqh10lj`zJA+?7=_M%hbehu5U4%_xBNoU(M{< zqi}7HKF`0v_}rdX8P4a)ahJ{{756`5e6HhF=XKpKo%<<WsBpI*rZSw*hihFr*D0Or z8K1WsW;oZm)unT*(z%`S`S=bnybgBjd2+S7`jY)Te%_{V-CrH&V=laxoPe}}=@`gs z{f!Lg@!^*Y=jW4MF8zq=*C6Bb{qzLGr?GbZWH^$$U+YTX|H$yaL_KZ)+YG;k;Q=~; zAd&ubd^F!=_zZ^M!*F_cq4^sb&d2u=g;Vns8tJc|DE%wr^Let9@o5ULBr?)|#^?I4 zFr3emqYUT#<1YR6QeBA87@ym7n&G^^=c<Di^^5F$T<O#(oczh#y^8V44|Ff3bZrTK zlZ#J(giL86<8wQ2VfbHxr^n@Y4Cm|RB*QOfe82qj5obPJ!|*1CPiJ_9;oLvJWH|T3 z(+YRbhvyid_iHc1xy~P$4*7pQK9r6yKGzv$IM?}->Cm`rR(>upKG#{S?*BBt+@DE? z^Z2ug;RfpHarr63`MLDp7|!F2{PRuJr*?Uqd6n_G{)Y_b_B7GvfkZkSzlY&`-2a*3 z^{Aum->PsujvGi6X*=U{`(I=@_y0kc&eO{O?=U`J*Pk(*{*6G}Q=#6QP`_p{{5*w| zom8gZr(VSPvlxF7!}&bFgW=r%)l8q}&rW537vuB!)5CD?Pn+Rf|3R0&j-QV(KG**Z z!@17yTsqp%11|h6W&d8LGn@HmJWW<4@?`@)+W&^a^?bOONJw>z&-c4)UHp}jEyQ<9 z@WU>CyW%e?!T*tqpH%#83I0!9{QpsYc&G$_2g9dATl?WvhI9WPV)_)X4k&xxWqdx* zC#n}c<Y%s5t8ndS`U^Ko2IF%-FJO28JZ<MPm(Cfba~I=tow$o%L53k&CHQ~m;!jZg ze<;EKHN&}`TU|Q6PP1Ys<8wP-W;i|S{!+}OL}u{yVi1T#-B%{cyuR;h+>odcx2a!^ zuT=RS7e1i!`dpy-hH7NLi@#ps^xmG5=Ii(UbuN6py64>H!gYLG@51$a`T-ZN-*caE R;rcyygR+Z?=~2I*{y&_))e!&y literal 0 HcmV?d00001 diff --git a/tc/m_estimator.c b/tc/m_estimator.c new file mode 100644 index 0000000..78eda7a --- /dev/null +++ b/tc/m_estimator.c @@ -0,0 +1,64 @@ +/* + * m_estimator.c Parse/print estimator module options. + * + * This program is free software; you can u32istribute 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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" + +static void est_help(void); + +static void est_help(void) +{ + fprintf(stderr, "Usage: ... estimator INTERVAL TIME-CONST\n"); + fprintf(stderr, " INTERVAL is interval between measurements\n"); + fprintf(stderr, " TIME-CONST is averaging time constant\n"); + fprintf(stderr, "Example: ... est 1sec 8sec\n"); + return; +} + +int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est) +{ + int argc = *p_argc; + char **argv = *p_argv; + unsigned A, time_const; + + NEXT_ARG(); + if (est->ewma_log) + duparg("estimator", *argv); + if (matches(*argv, "help") == 0) + est_help(); + if (get_usecs(&A, *argv)) + invarg("estimator", "invalid estimator interval"); + NEXT_ARG(); + if (matches(*argv, "help") == 0) + est_help(); + if (get_usecs(&time_const, *argv)) + invarg("estimator", "invalid estimator time constant"); + if (tc_setup_estimator(A, time_const, est) < 0) { + fprintf(stderr, "Error: estimator parameters are out of range.\n"); + return -1; + } + if (show_raw) + fprintf(stderr, "[estimator i=%u e=%u]\n", est->interval, est->ewma_log); + *p_argc = argc; + *p_argv = argv; + return 0; +} diff --git a/tc/m_estimator.o b/tc/m_estimator.o new file mode 100644 index 0000000000000000000000000000000000000000..b64697e6d4a09b57340b87580ccea96c6b8188aa GIT binary patch literal 3552 zcmbVOO>7%Q6du=U>!u+NC7=jU8PI}Ki7atal_=2SD1@%jl8QD(p#HcUd+b>FPrKtL z{78)y9N{jhB5|p>^#m8BiUS}HX{1)-07x7-AT9+?iAoV%P!523Z#-j9Hnx!Zq@8*5 zz2ABBx0j~umv)6hL{W$wBdw7_30dfB@o~Y%NiW$?tfk)~mVX~2Umb#G4UKQ$a0449 zdK&l+qQTChN4ANqrCX6rKu9K&Syi81OB)+@EpPmhsQqBqxUpr`&Kf@^YX4XO{tW4j z`OjGX!9?w^`ghjS%}Dib^vdtxWby3~iB`9Ov;61Q(5*<gx*mP+ssKl=m3NIE2?~X1 z6(`nm^Rdv|X3R^LFAO8fTXh+<d~QU1@MhP3Rrvdt5#J3BsjgREn5Y#<qINL0Zuw`8 zKFiM=1Gb+ws14kp4few@{jr;?8~(rb`sVq1y)KHdy73N-?_8dUer_9~iRfCtAyT#c zqv$!dF2dcpCORR}*Je~7G}h1rtH45i3Bl@{X=p%p+H<mOoSLRdna6VlhnHNMn4XzB z`Pzg{XA-YW4NabyJ~h*g;elvNnDc0^$eDZ5$<tZJ=NT)~0&~2I%L=T>Jz+vM9~3N{ zi_CShxnh<=Fh*0QqQ{*gSMEu!2<~!eEtze4WXg3*?zko{JFZh;kOug687);fEoG?d z6tm3iCQ}Pep`33`kj6ZgqGQl>lR1_zw~43bM9#Ddk~{ijg)(T)b(1D4>d`M+0%t6= z(ATp!vMLYTqk!Y5h)d?h-af$@&_1t(&=;iL%PZjzy58uGFYjL3b7dC@j!#aG)4}7@ zrw`Fl^RPKWM}~(F$A)9i(!rA~O)ZBv@S!7SCN&C2*7V*gaAy{J?l$^4#knl+n4B$e zVx}GL5Odb^i0PJafa)=GZ2K?<L$qOY$qbg6n8gxjCS)@N%a(eRn5j|$7h9Xjy|EO` zumpPo=5aZ`E9;}gjVh`UA$YZDS6WkWRqm{Ta0BXr4qZj0#(?aflDJfpC?UhR25l%9 z3ok_Ud9U^{u*JMa;L}OPzpaD7tNc}2;()cYkIID31?-ib>Sv^VaJpq_e<5J6;w$(B zFu|Ij?7FyyppH@z>%SL1Wv}Xg9x#=QR2AL#!GcO*P`CI$_E=w?g7)`=uwAH-oYcH- z41CZY$HCg6$rer)s80&99Q)x5&VL_Z?czt|{#^mNPQm!POy^Ori~NaK{SFH6m_<V+ z+%^-;uiXkGE}p$M{)4BPkSJ*F(&FkjM_BBP9d5usR%a)LN%Prg3UNOL?*<e;)&a+J ze*p4XgdR!V!$*K`=jT`l-0Faz>42v@;Q0>tr4IOV2b>g=&4<cN;iHul@14if%ymg- z-pz4F;PLcWOOe3ypPFMH$ugd-z$57qo=SR*SIU}_H&>cZy3RbwlwEi%Gw_DOD*&%7 zXGwexoMKu$t8SL0D-a<IDK=LYj{)xgzwsvo=phsW;Y#Ye2=8+w)W<tR=|6!WP|%M$ z-^!1=Z*U!vf_}vBl*ILXY@yxC=Ykx+D*K?H*EL-CBPI<p%tt>3UrT+3tAB)VH9x(; ztNH$-;dpiwz9Dht=W#jik{5%*mH&Mb$NY8w4{NyYe?-Ia9H_W)4aYlE;U7v|#Z~_{ zA8Gn}eXeQv0nN`R8vcNWf2#S%eX<H4(q|fubv`9fLcY-a^aG~yywO4bmZtxprvHtG l>-qnr`N4dy%ec2SeSP1Sr98NQsKn6>Ws2gGQTU=9{|9}RKF0t6 literal 0 HcmV?d00001 diff --git a/tc/m_gact.c b/tc/m_gact.c new file mode 100644 index 0000000..4bb5041 --- /dev/null +++ b/tc/m_gact.c @@ -0,0 +1,244 @@ +/* + * m_gact.c generic actions module + * + * 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 (hadi@cyberus.ca) + * + */ + +#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_gact.h> + +/* define to turn on probablity stuff */ + +#ifdef CONFIG_GACT_PROB +static const char *prob_n2a(int p) +{ + if (p == PGACT_NONE) + return "none"; + if (p == PGACT_NETRAND) + return "netrand"; + if (p == PGACT_DETERM) + return "determ"; + return "none"; +} +#endif + +static void +explain(void) +{ +#ifdef CONFIG_GACT_PROB + fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [INDEX]\n"); + fprintf(stderr, + "Where: ACTION := reclassify | drop | continue | pass " + "RAND := random <RANDTYPE> <ACTION> <VAL>" + "RANDTYPE := netrand | determ" + "VAL : = value not exceeding 10000" + "INDEX := index value used" + "\n"); +#else + fprintf(stderr, "Usage: ... gact <ACTION> [INDEX]\n"); + fprintf(stderr, + "Where: ACTION := reclassify | drop | continue | pass " + "INDEX := index value used" + "\n"); +#endif +} + +#define usage() return(-1) + +int +get_act(char ***argv_p) +{ + char **argv = *argv_p; + + if (matches(*argv, "reclassify") == 0) { + return TC_ACT_RECLASSIFY; + } else if (matches(*argv, "drop") == 0 || matches(*argv, "shot") == 0) { + return TC_ACT_SHOT; + } else if (matches(*argv, "continue") == 0) { + return TC_ACT_UNSPEC; + } else if (matches(*argv, "pipe") == 0) { + return TC_ACT_PIPE; + } else if (matches(*argv, "pass") == 0 || matches(*argv, "ok") == 0) { + return TC_ACT_OK; + } else { + fprintf(stderr,"bad action type %s\n",*argv); + return -10; + } +} + +int +parse_gact(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0; + int action = TC_POLICE_RECLASSIFY; + struct tc_gact p; +#ifdef CONFIG_GACT_PROB + int rd = 0; + struct tc_gact_p pp; +#endif + struct rtattr *tail; + + memset(&p, 0, sizeof (p)); + p.action = TC_POLICE_RECLASSIFY; + + if (argc < 0) + return -1; + + + if (matches(*argv, "gact") == 0) { + ok++; + } else { + action = get_act(&argv); + if (action != -10) { + p.action = action; + ok++; + } else { + explain(); + return action; + } + } + + if (ok) { + argc--; + argv++; + } + +#ifdef CONFIG_GACT_PROB + if (ok && argc > 0) { + if (matches(*argv, "random") == 0) { + rd = 1; + NEXT_ARG(); + if (matches(*argv, "netrand") == 0) { + NEXT_ARG(); + pp.ptype = PGACT_NETRAND; + } else if (matches(*argv, "determ") == 0) { + NEXT_ARG(); + pp.ptype = PGACT_DETERM; + } else { + fprintf(stderr, "Illegal \"random type\"\n"); + return -1; + } + + action = get_act(&argv); + if (action != -10) { /* FIXME */ + pp.paction = action; + } else { + explain(); + return -1; + } + argc--; + argv++; + if (get_u16(&pp.pval, *argv, 10)) { + fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + return -1; + } + if (pp.pval > 10000) { + fprintf(stderr, "Illegal probability val 0x%x\n",pp.pval); + return -1; + } + argc--; + argv++; + } + } +#endif + + if (argc > 0) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + ok++; + } + } + + if (!ok) + return -1; + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof (p)); +#ifdef CONFIG_GACT_PROB + if (rd) { + addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof (pp)); + } +#endif + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +int +print_gact(struct action_util *au,FILE * f, struct rtattr *arg) +{ + SPRINT_BUF(b1); +#ifdef CONFIG_GACT_PROB + SPRINT_BUF(b2); + struct tc_gact_p *pp = NULL; + struct tc_gact_p pp_dummy; +#endif + struct tc_gact *p = NULL; + struct rtattr *tb[TCA_GACT_MAX + 1]; + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_GACT_MAX, arg); + + if (tb[TCA_GACT_PARMS] == NULL) { + fprintf(f, "[NULL gact parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_GACT_PARMS]); + + fprintf(f, "gact action %s", action_n2a(p->action, b1, sizeof (b1))); +#ifdef CONFIG_GACT_PROB + if (NULL != tb[TCA_GACT_PROB]) { + pp = RTA_DATA(tb[TCA_GACT_PROB]); + } else { + /* need to keep consistent output */ + memset(&pp_dummy, 0, sizeof (pp_dummy)); + pp = &pp_dummy; + } + fprintf(f, "\n\t random type %s %s val %d",prob_n2a(pp->ptype), action_n2a(pp->paction, b2, sizeof (b2)), pp->pval); +#endif + fprintf(f, "\n\t index %d ref %d bind %d",p->index, p->refcnt, p->bindcnt); + if (show_stats) { + if (tb[TCA_GACT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_GACT_TM]); + print_tm(f,tm); + } + } + fprintf(f, "\n "); + return 0; +} + +struct action_util gact_action_util = { + .id = "gact", + .parse_aopt = parse_gact, + .print_aopt = print_gact, +}; diff --git a/tc/m_gact.o b/tc/m_gact.o new file mode 100644 index 0000000000000000000000000000000000000000..6a26b0111099fd98979244ebb1814e8f5b30b893 GIT binary patch literal 6128 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 literal 0 HcmV?d00001 diff --git a/tc/m_ipt.c b/tc/m_ipt.c new file mode 100644 index 0000000..518e4a3 --- /dev/null +++ b/tc/m_ipt.c @@ -0,0 +1,599 @@ +/* + * m_ipt.c iptables based targets + * utilities mostly ripped from iptables <duh, its the linux way> + * + * 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 (hadi@cyberus.ca) + * + * TODO: bad bad hardcoding IPT_LIB_DIR and PROC_SYS_MODPROBE + * +*/ + +#include <syslog.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <iptables.h> +#include <linux/netfilter_ipv4/ip_tables.h> +#include "utils.h" +#include "tc_util.h" +#include <linux/tc_act/tc_ipt.h> +#include <stdio.h> +#include <dlfcn.h> +#include <getopt.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> + +const char *pname = "tc-ipt"; +const char *tname = "mangle"; +const char *pversion = "0.1"; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/local/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +static const char *ipthooks[] = { + "NF_IP_PRE_ROUTING", + "NF_IP_LOCAL_IN", + "NF_IP_FORWARD", + "NF_IP_LOCAL_OUT", + "NF_IP_POST_ROUTING", +}; + +static struct option original_opts[] = { + {"jump", 1, 0, 'j'}, + {0, 0, 0, 0} +}; + +static struct iptables_target *t_list = NULL; +static unsigned int global_option_offset = 0; +#define OPTION_OFFSET 256 + + +void +register_target(struct iptables_target *me) +{ +/* fprintf(stderr, "\nDummy register_target %s \n", me->name); +*/ + me->next = t_list; + t_list = me; + +} + +void +exit_tryhelp(int status) +{ + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + pname, pname); + exit(status); +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", pname, pversion); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +/* stolen from iptables 1.2.11 +They should really have them as a library so i can link to them +Email them next time i remember +*/ + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +int string_to_number_ll(const char *s, unsigned long long min, + unsigned long long max, + unsigned long long *ret) +{ + unsigned long long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtoull(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && (!max || number <= max)) { + *ret = number; + return 0; + } + } + return -1; +} + +int string_to_number_l(const char *s, unsigned long min, unsigned long max, + unsigned long *ret) +{ + int result; + unsigned long long number; + + result = string_to_number_ll(s, min, max, &number); + *ret = (unsigned long)number; + + return result; +} + +int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + int result; + unsigned long number; + + result = string_to_number_l(s, min, max, &number); + *ret = (unsigned int)number; + + return result; +} + +static struct option * +copy_options(struct option *oldopts) +{ + struct option *merge; + unsigned int num_old; + for (num_old = 0; oldopts[num_old].name; num_old++) ; + merge = malloc(sizeof (struct option) * (num_old + 1)); + if (NULL == merge) + return NULL; + memcpy(merge, oldopts, num_old * sizeof (struct option)); + memset(merge + num_old, 0, sizeof (struct option)); + return merge; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + struct option *merge; + unsigned int num_old, num_new, i; + + for (num_old = 0; oldopts[num_old].name; num_old++) ; + for (num_new = 0; newopts[num_new].name; num_new++) ; + + *option_offset = global_option_offset + OPTION_OFFSET; + + merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof (struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof (struct option)); + + return merge; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = (void *) calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static struct iptables_target * +find_t(char *name) +{ + struct iptables_target *m; + for (m = t_list; m; m = m->next) { + if (strcmp(m->name, name) == 0) + return m; + } + + return NULL; +} + +static struct iptables_target * +get_target_name(char *name) +{ + void *handle; + char *error; + char *new_name, *lname; + struct iptables_target *m; + + char path[sizeof (IPT_LIB_DIR) + sizeof ("/libipt_.so") + strlen(name)]; + + new_name = malloc(strlen(name) + 1); + lname = malloc(strlen(name) + 1); + if (new_name) + memset(new_name, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + if (lname) + memset(lname, '\0', strlen(name) + 1); + else + exit_error(PARAMETER_PROBLEM, "get_target_name"); + + strcpy(new_name, name); + strcpy(lname, name); + + if (isupper(lname[0])) { + int i; + for (i = 0; i < strlen(name); i++) { + lname[i] = tolower(lname[i]); + } + } + + if (islower(new_name[0])) { + int i; + for (i = 0; i < strlen(new_name); i++) { + new_name[i] = toupper(new_name[i]); + } + } + + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", new_name); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", lname); + handle = dlopen(path, RTLD_LAZY); + if (!handle) { + fputs(dlerror(), stderr); + printf("\n"); + return NULL; + } + } + + m = dlsym(handle, new_name); + if ((error = dlerror()) != NULL) { + m = (struct iptables_target *) dlsym(handle, lname); + if ((error = dlerror()) != NULL) { + m = find_t(new_name); + if (NULL == m) { + m = find_t(lname); + if (NULL == m) { + fputs(error, stderr); + fprintf(stderr, "\n"); + dlclose(handle); + return NULL; + } + } + } + } + + return m; +} + + +struct in_addr *dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof (buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +int +build_st(struct iptables_target *target, struct ipt_entry_target *t) +{ + unsigned int nfcache = 0; + + if (target) { + size_t size; + + size = + IPT_ALIGN(sizeof (struct ipt_entry_target)) + target->size; + + if (NULL == t) { + target->t = fw_calloc(1, size); + target->init(target->t, &nfcache); + target->t->u.target_size = size; + } else { + target->t = t; + } + strcpy(target->t->u.user.name, target->name); + return 0; + } + + return -1; +} + +static int parse_ipt(struct action_util *a,int *argc_p, + char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + struct iptables_target *m = NULL; + struct ipt_entry fw; + struct rtattr *tail; + int c; + int rargc = *argc_p; + char **argv = *argv_p; + struct option *opts; + int argc = 0, iargc = 0; + char k[16]; + int res = -1; + int size = 0; + int iok = 0, ok = 0; + __u32 hook = 0, index = 0; + res = 0; + + { + int i; + for (i = 0; i < rargc; i++) { + if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { + break; + } + } + iargc = argc = i; + } + + if (argc <= 2) { + fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc); + return -1; + } + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + while (1) { + c = getopt_long(argc, argv, "j:", opts, NULL); + if (c == -1) + break; + switch (c) { + case 'j': + m = get_target_name(optarg); + if (NULL != m) { + + if (0 > build_st(m, NULL)) { + printf(" %s error \n", m->name); + return -1; + } + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + } + ok++; + break; + + default: + memset(&fw, 0, sizeof (fw)); + if (m) { + unsigned int fake_flags = 0; + m->parse(c - m->option_offset, argv, 0, + &fake_flags, NULL, &m->t); + } else { + fprintf(stderr," failed to find target %s\n\n", optarg); + return -1; + + } + ok++; + + /*m->final_check(m->t); -- Is this necessary? + ** useful when theres depencies + ** eg ipt_TCPMSS.c has have the TCP match loaded + ** before this can be used; + ** also seems the ECN target needs it + */ + + break; + + } + } + + if (iargc > optind) { + if (matches(argv[optind], "index") == 0) { + if (get_u32(&index, argv[optind + 1], 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + + optind += 2; + } + } + + if (!ok && !iok) { + fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); + return -1; + } + + { + struct tcmsg *t = NLMSG_DATA(n); + if (t->tcm_parent != TC_H_ROOT + && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { + hook = NF_IP_PRE_ROUTING; + } else { + hook = NF_IP_POST_ROUTING; + } + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); + fprintf(stdout, "\ttarget: "); + + if (m) + m->print(NULL, m->t, 0); + fprintf(stdout, " index %d\n", index); + + if (strlen(tname) > 16) { + size = 16; + k[15] = 0; + } else { + size = 1 + strlen(tname); + } + strncpy(k, tname, size); + + addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); + addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); + addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); + if (m) + addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + argc -= optind; + argv += optind; + *argc_p = rargc - iargc; + *argv_p = argv; + + optind = 1; + + return 0; + +} + +static int +print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct rtattr *tb[TCA_IPT_MAX + 1]; + struct ipt_entry_target *t = NULL; + struct option *opts; + + if (arg == NULL) + return -1; + + opts = copy_options(original_opts); + + if (NULL == opts) + return -1; + + parse_rtattr_nested(tb, TCA_IPT_MAX, arg); + + if (tb[TCA_IPT_TABLE] == NULL) { + fprintf(f, "[NULL ipt table name ] assuming mangle "); + } else { + fprintf(f, "tablename: %s ", + (char *) RTA_DATA(tb[TCA_IPT_TABLE])); + } + + if (tb[TCA_IPT_HOOK] == NULL) { + fprintf(f, "[NULL ipt hook name ]\n "); + return -1; + } else { + __u32 hook; + hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]); + fprintf(f, " hook: %s \n", ipthooks[hook]); + } + + if (tb[TCA_IPT_TARG] == NULL) { + fprintf(f, "\t[NULL ipt target parameters ] \n"); + return -1; + } else { + struct iptables_target *m = NULL; + t = RTA_DATA(tb[TCA_IPT_TARG]); + m = get_target_name(t->u.user.name); + if (NULL != m) { + if (0 > build_st(m, t)) { + fprintf(stderr, " %s error \n", m->name); + return -1; + } + + opts = + merge_options(opts, m->extra_opts, + &m->option_offset); + } else { + fprintf(stderr, " failed to find target %s\n\n", + t->u.user.name); + return -1; + } + fprintf(f, "\ttarget "); + m->print(NULL, m->t, 0); + if (tb[TCA_IPT_INDEX] == NULL) { + fprintf(f, " [NULL ipt target index ]\n"); + } else { + __u32 index; + index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]); + fprintf(f, " \n\tindex %d", index); + } + + if (tb[TCA_IPT_CNT]) { + struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; + fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); + } + if (show_stats) { + if (tb[TCA_IPT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); + print_tm(f,tm); + } + } + fprintf(f, " \n"); + + } + + return 0; +} + +struct action_util ipt_action_util = { + .id = "ipt", + .parse_aopt = parse_ipt, + .print_aopt = print_ipt, +}; + diff --git a/tc/m_ipt.o b/tc/m_ipt.o new file mode 100644 index 0000000000000000000000000000000000000000..8ee39ea12d3e276ce898f82f660bbcb93465afd4 GIT binary patch literal 13552 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 C#qi1i literal 0 HcmV?d00001 diff --git a/tc/m_mirred.c b/tc/m_mirred.c new file mode 100644 index 0000000..6ade2a8 --- /dev/null +++ b/tc/m_mirred.c @@ -0,0 +1,292 @@ +/* + * m_egress.c ingress/egress packet mirror/redir actions module + * + * 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 (hadi@cyberus.ca) + * + * TODO: Add Ingress support + * + */ + +#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 "tc_common.h" +#include <linux/tc_act/tc_mirred.h> + +int mirred_d = 1; + +static void +explain(void) +{ + fprintf(stderr, "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev DEVICENAME> \n"); + fprintf(stderr, "where: \n"); + fprintf(stderr, "DIRECTION := <ingress | egress>\n"); + fprintf(stderr, "aCTION := <mirror | redirect>\n"); + fprintf(stderr, " : INDEX is the specific policy instance id\n"); + fprintf(stderr, " : DEVICENAME is the devicename \n"); +} + +#define usage() return(-1) + +char *mirred_n2a(int action) +{ + switch (action) { + case TCA_EGRESS_REDIR: + return "Egress Redirect"; + case TCA_INGRESS_REDIR: + return "Ingress Redirect"; + case TCA_EGRESS_MIRROR: + return "Egress Mirror"; + case TCA_INGRESS_MIRROR: + return "Ingress Mirror"; + default: + return "unknown"; + } +} + +int +parse_egress(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0, iok = 0, mirror=0,redir=0; + struct tc_mirred p; + struct rtattr *tail; + char d[16]; + + memset(d,0,sizeof(d)-1); + memset(&p,0,sizeof(struct tc_mirred)); + + while (argc > 0) { + + if (matches(*argv, "action") == 0) { + break; + } else if (matches(*argv, "egress") == 0) { + NEXT_ARG(); + ok++; + continue; + } else { + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + iok++; + if (!ok) { + argc--; + argv++; + break; + } + } else if(!ok) { + fprintf(stderr, "was expecting egress (%s)\n", *argv); + break; + + } else if (!mirror && matches(*argv, "mirror") == 0) { + mirror=1; + if (redir) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_MIRROR; + p.action = TC_ACT_PIPE; + ok++; + } else if (!redir && matches(*argv, "redirect") == 0) { + redir=1; + if (mirror) { + fprintf(stderr, "Cant have both mirror and redir\n"); + return -1; + } + p.eaction = TCA_EGRESS_REDIR; + p.action = TC_ACT_STOLEN; + ok++; + } else if ((redir || mirror) && matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (strlen(d)) + duparg("dev", *argv); + + strncpy(d, *argv, sizeof(d)-1); + argc--; + argv++; + + break; + + } + } + + NEXT_ARG(); + } + + if (!ok && !iok) { + explain(); + return -1; + } + + + + if (d[0]) { + int idx; + ll_init_map(&rth); + + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; + } + + p.ifindex = idx; + } + + + if (argc && p.eaction == TCA_EGRESS_MIRROR) { + + if (matches(*argv, "reclassify") == 0) { + p.action = TC_POLICE_RECLASSIFY; + NEXT_ARG(); + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_POLICE_PIPE; + NEXT_ARG(); + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_POLICE_SHOT; + NEXT_ARG(); + } else if (matches(*argv, "continue") == 0) { + p.action = TC_POLICE_UNSPEC; + NEXT_ARG(); + } else if (matches(*argv, "pass") == 0) { + p.action = TC_POLICE_OK; + NEXT_ARG(); + } + + } + + if (argc) { + if (iok && matches(*argv, "index") == 0) { + fprintf(stderr, "mirred: Illegal double index\n"); + return -1; + } else { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "mirred: Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + } + } + } + + if (mirred_d) + fprintf(stdout, "Action %d device %s ifindex %d\n",p.action, d,p.ifindex); + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof (p)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + + +int +parse_mirred(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 0) { + fprintf(stderr,"mirred bad arguement count %d\n", argc); + return -1; + } + + if (matches(*argv, "mirred") == 0) { + NEXT_ARG(); + } else { + fprintf(stderr,"mirred bad arguement %s\n", *argv); + return -1; + } + + + if (matches(*argv, "egress") == 0 || matches(*argv, "index") == 0) { + int ret = parse_egress(a, &argc, &argv, tca_id, n); + if (ret == 0) { + *argc_p = argc; + *argv_p = argv; + return 0; + } + + } else if (matches(*argv, "ingress") == 0) { + fprintf(stderr,"mirred ingress not supported at the moment\n"); + + } else { + fprintf(stderr,"mirred not supported %s\n", *argv); + } + + return -1; + +} + +int +print_mirred(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct tc_mirred *p; + struct rtattr *tb[TCA_MIRRED_MAX + 1]; + const char *dev; + SPRINT_BUF(b1); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg); + + if (tb[TCA_MIRRED_PARMS] == NULL) { + fprintf(f, "[NULL mirred parameters]"); + return -1; + } + p = RTA_DATA(tb[TCA_MIRRED_PARMS]); + + ll_init_map(&rth); + + if ((dev = ll_index_to_name(p->ifindex)) == 0) { + fprintf(stderr, "Cannot find device %d\n", p->ifindex); + return -1; + } + + fprintf(f, "mirred (%s to device %s) %s", mirred_n2a(p->eaction), dev,action_n2a(p->action, b1, sizeof (b1))); + + fprintf(f, "\n "); + fprintf(f, "\tindex %d ref %d bind %d",p->index,p->refcnt,p->bindcnt); + + if (show_stats) { + if (tb[TCA_MIRRED_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_MIRRED_TM]); + print_tm(f,tm); + } + } + fprintf(f, "\n "); + return 0; +} + +struct action_util mirred_util_util = { + .id = "mirred", + .parse_aopt = parse_mirred, + .print_aopt = print_mirred, +}; diff --git a/tc/m_mirred.o b/tc/m_mirred.o new file mode 100644 index 0000000000000000000000000000000000000000..f04a75508a56bf86c6eeeb722bd8ad596a87f65e GIT binary patch literal 9336 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 E1*UB#s{jB1 literal 0 HcmV?d00001 diff --git a/tc/m_pedit.c b/tc/m_pedit.c new file mode 100644 index 0000000..5031c62 --- /dev/null +++ b/tc/m_pedit.c @@ -0,0 +1,604 @@ +/* + * m_pedit.c generic packet editor actions module + * + * 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 (hadi@cyberus.ca) + * + * TODO: + * 1) Big endian broken in some spots + * 2) A lot of this stuff was added on the fly; get a big double-double + * and clean it up at some point. + * + */ + +#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 <dlfcn.h> +#include "utils.h" +#include "tc_util.h" +#include "m_pedit.h" + +static struct m_pedit_util *pedit_list; +int pedit_debug = 1; + +static void +p_explain(void) +{ + fprintf(stderr, "Usage: ... pedit <MUNGE>\n"); + fprintf(stderr, + "Where: MUNGE := <RAW>|<LAYERED>\n" + "<RAW>:= <OFFSETC>[ATC]<CMD>\n " + "OFFSETC:= offset <offval> <u8|u16|u32>\n " + "ATC:= at <atval> offmask <maskval> shift <shiftval>\n " + "NOTE: offval is byte offset, must be multiple of 4\n " + "NOTE: maskval is a 32 bit hex number\n " + "NOTE: shiftval is a is a shift value\n " + "CMD:= clear | invert | set <setval>| retain\n " + "<LAYERED>:= ip <ipdata> | ip6 <ip6data> \n " + " | udp <udpdata> | tcp <tcpdata> | icmp <icmpdata> \n" + "For Example usage look at the examples directory"); + +} + +#define usage() return(-1) + +static int +pedit_parse_nopopt (int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc) { + fprintf(stderr, "Unknown action hence option \"%s\" is unparsable\n", *argv); + return -1; + } + + return 0; + +} + +struct m_pedit_util +*get_pedit_kind(char *str) +{ + static void *pBODY; + void *dlh; + char buf[256]; + struct m_pedit_util *p; + + for (p = pedit_list; p; p = p->next) { + if (strcmp(p->id, str) == 0) + return p; + } + + snprintf(buf, sizeof(buf), "p_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = pBODY; + if (dlh == NULL) { + dlh = pBODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "p_pedit_%s", str); + p = dlsym(dlh, buf); + if (p == NULL) + goto noexist; + +reg: + p->next = pedit_list; + pedit_list = p; + return p; + +noexist: + p = malloc(sizeof(*p)); + if (p) { + memset(p, 0, sizeof(*p)); + strncpy(p->id, str, sizeof(p->id)-1); + p->parse_peopt = pedit_parse_nopopt; + goto reg; + } + return p; +} + +int +pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int hwm = sel->nkeys; + + if (hwm >= MAX_OFFS) + return -1; + + if (tkey->off % 4) { + fprintf(stderr, "offsets MUST be in 32 bit boundaries\n"); + return -1; + } + + sel->keys[hwm].val = tkey->val; + sel->keys[hwm].mask = tkey->mask; + sel->keys[hwm].off = tkey->off; + sel->keys[hwm].at = tkey->at; + sel->keys[hwm].offmask = tkey->offmask; + sel->keys[hwm].shift = tkey->shift; + sel->nkeys++; + return 0; +} + + +int +pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + if (tkey->off > (tkey->off & ~3)) { + fprintf(stderr, + "pack_key32: 32 bit offsets must begin in 32bit boundaries\n"); + return -1; + } + + tkey->val = htonl(tkey->val & retain); + tkey->mask = htonl(tkey->mask | ~retain); + /* jamal remove this - it is not necessary given the if check above */ + tkey->off &= ~3; + return pack_key(sel,tkey); +} + +int +pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int ind = 0, stride = 0; + __u32 m[4] = {0xFFFF0000,0xFF0000FF,0x0000FFFF}; + + if (0 > tkey->off) { + ind = tkey->off + 1; + if (0 > ind) + ind = -1*ind; + } else { + ind = tkey->off; + } + + if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) { + fprintf(stderr, "pack_key16 bad value\n"); + return -1; + } + + ind = tkey->off & 3; + + if (0 > ind || 2 < ind) { + fprintf(stderr, "pack_key16 bad index value %d\n",ind); + return -1; + } + + stride = 8 * ind; + tkey->val = htons(tkey->val); + if (stride > 0) { + tkey->val <<= stride; + tkey->mask <<= stride; + retain <<= stride; + } + tkey->mask = retain|m[ind]; + + tkey->off &= ~3; + + if (pedit_debug) + printf("pack_key16: Final val %08x mask %08x \n",tkey->val,tkey->mask); + return pack_key(sel,tkey); + +} + +int +pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int ind = 0, stride = 0; + __u32 m[4] = {0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,0x00FFFFFF}; + + if (0 > tkey->off) { + ind = tkey->off + 1; + if (0 > ind) + ind = -1*ind; + } else { + ind = tkey->off; + } + + if (tkey->val > 0xFF || tkey->mask > 0xFF) { + fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", tkey->val, tkey->mask); + return -1; + } + + ind = tkey->off & 3; + stride = 8 * ind; + tkey->val <<= stride; + tkey->mask <<= stride; + retain <<= stride; + tkey->mask = retain|m[ind]; + tkey->off &= ~3; + + if (pedit_debug) + printf("pack_key8: Final word off %d val %08x mask %08x \n",tkey->off , tkey->val,tkey->mask); + return pack_key(sel,tkey); +} + +int +parse_val(int *argc_p, char ***argv_p, __u32 * val, int type) +{ + int argc = *argc_p; + char **argv = *argv_p; + + if (argc <= 0) + return -1; + + if (TINT == type) + return get_integer(val, *argv, 0); + if (TU32 == type) + return get_u32(val, *argv, 0); + if (TIPV4 == type) { + inet_prefix addr; + if (get_prefix_1(&addr, *argv, AF_INET)) { + return -1; + } + *val=addr.data[0]; + return 0; + } + if (TIPV6 == type) { + /* not implemented yet */ + return -1; + } + + return -1; +} + +int +parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + __u32 mask = 0, val = 0; + __u32 o = 0xFF; + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc <= 0) + return -1; + + if (pedit_debug) + printf("parse_cmd argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + + if (len == 2) + o = 0xFFFF; + if (len == 4) + o = 0xFFFFFFFF; + + if (matches(*argv, "invert") == 0) { + retain = val = mask = o; + } else if (matches(*argv, "set") == 0) { + NEXT_ARG(); + if (parse_val(&argc, &argv, &val, type)) + return -1; + } else if (matches(*argv, "preserve") == 0) { + retain = mask = o; + } else { + if (matches(*argv, "clear") != 0) + return -1; + } + + argc--; argv++; + + if (argc && matches(*argv, "retain") == 0) { + NEXT_ARG(); + if (parse_val(&argc, &argv, &retain, TU32)) + return -1; + argc--; argv++; + } + + tkey->val = val; + + if (len == 1) { + tkey->mask = 0xFF; + res = pack_key8(retain,sel,tkey); + goto done; + } + if (len == 2) { + tkey->mask = mask; + res = pack_key16(retain,sel,tkey); + goto done; + } + if (len == 4) { + tkey->mask = mask; + res = pack_key32(retain,sel,tkey); + goto done; + } + + return -1; +done: + if (pedit_debug) + printf("parse_cmd done argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); + *argc_p = argc; + *argv_p = argv; + return res; + +} + +int +parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int off; + __u32 len, retain; + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + if (get_integer(&off, *argv, 0)) + return -1; + tkey->off = off; + + argc--; + argv++; + + if (argc <= 0) + return -1; + + + if (matches(*argv, "u32") == 0) { + len = 4; + retain = 0xFFFFFFFF; + goto done; + } + if (matches(*argv, "u16") == 0) { + len = 2; + retain = 0x0; + goto done; + } + if (matches(*argv, "u8") == 0) { + len = 1; + retain = 0x0; + goto done; + } + + return -1; + +done: + + NEXT_ARG(); + + /* [at <someval> offmask <maskval> shift <shiftval>] */ + if (matches(*argv, "at") == 0) { + + __u32 atv=0,offmask=0x0,shift=0; + + NEXT_ARG(); + if (get_u32(&atv, *argv, 0)) + return -1; + tkey->at = atv; + + NEXT_ARG(); + + if (get_u32(&offmask, *argv, 16)) + return -1; + tkey->offmask = offmask; + + NEXT_ARG(); + + if (get_u32(&shift, *argv, 0)) + return -1; + tkey->shift = shift; + + NEXT_ARG(); + } + + res = parse_cmd(&argc, &argv, len, TU32,retain,sel,tkey); + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int +parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel) +{ + struct tc_pedit_key tkey; + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + + if (argc <= 0) + return -1; + + memset(&tkey, 0, sizeof(tkey)); + + if (matches(*argv, "offset") == 0) { + NEXT_ARG(); + res = parse_offset(&argc, &argv,sel,&tkey); + goto done; +#if jamal + } else if (strcmp(*argv, "help") == 0) { + p_explain(); + return -1; +#endif + } else { + char k[16]; + struct m_pedit_util *p = NULL; + + strncpy(k, *argv, sizeof (k) - 1); + + if (argc > 0 ) { + p = get_pedit_kind(k); + if (NULL == p) + goto bad_val; + res = p->parse_peopt(&argc, &argv, sel,&tkey); + if (res < 0) { + fprintf(stderr,"bad pedit parsing\n"); + goto bad_val; + } + goto done; + } + } + +bad_val: + return -1; + +done: + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int +parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + struct { + struct tc_pedit_sel sel; + struct tc_pedit_key keys[MAX_OFFS]; + } sel; + + int argc = *argc_p; + char **argv = *argv_p; + int ok = 0, iok = 0; + struct rtattr *tail; + + memset(&sel, 0, sizeof(sel)); + + while (argc > 0) { + if (pedit_debug > 1) + fprintf(stderr, "while pedit (%d:%s)\n",argc, *argv); + if (matches(*argv, "pedit") == 0) { + NEXT_ARG(); + ok++; + continue; + } else if (matches(*argv, "munge") == 0) { + if (!ok) { + fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + p_explain(); + return -1; + } + NEXT_ARG(); + if (parse_munge(&argc, &argv,&sel.sel)) { + fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); + p_explain(); + return -1; + } + ok++; + } else { + break; + } + + } + + if (!ok) { + p_explain(); + return -1; + } + + if (argc) { + if (matches(*argv, "reclassify") == 0) { + sel.sel.action = TC_ACT_RECLASSIFY; + NEXT_ARG(); + } else if (matches(*argv, "pipe") == 0) { + sel.sel.action = TC_ACT_PIPE; + NEXT_ARG(); + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + sel.sel.action = TC_ACT_SHOT; + NEXT_ARG(); + } else if (matches(*argv, "continue") == 0) { + sel.sel.action = TC_ACT_UNSPEC; + NEXT_ARG(); + } else if (matches(*argv, "pass") == 0) { + sel.sel.action = TC_ACT_OK; + NEXT_ARG(); + } + } + + if (argc) { + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&sel.sel.index, *argv, 10)) { + fprintf(stderr, "Pedit: Illegal \"index\"\n"); + return -1; + } + argc--; + argv++; + iok++; + } + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS,&sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_pedit_key)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + + *argc_p = argc; + *argv_p = argv; + return 0; +} + +int +print_pedit(struct action_util *au,FILE * f, struct rtattr *arg) +{ + struct tc_pedit_sel *sel; + struct rtattr *tb[TCA_PEDIT_MAX + 1]; + SPRINT_BUF(b1); + + if (arg == NULL) + return -1; + + parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg); + + if (tb[TCA_PEDIT_PARMS] == NULL) { + fprintf(f, "[NULL pedit parameters]"); + return -1; + } + sel = RTA_DATA(tb[TCA_PEDIT_PARMS]); + + fprintf(f, " pedit action %s keys %d\n ", action_n2a(sel->action, b1, sizeof (b1)),sel->nkeys); + fprintf(f, "\t index %d ref %d bind %d", sel->index,sel->refcnt, sel->bindcnt); + + if (show_stats) { + if (tb[TCA_PEDIT_TM]) { + struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]); + print_tm(f,tm); + } + } + if (sel->nkeys) { + int i; + struct tc_pedit_key *key = sel->keys; + + for (i=0; i<sel->nkeys; i++, key++) { + fprintf(f, "\n\t key #%d",i); + fprintf(f, " at %d: val %08x mask %08x", + (unsigned int)key->off, + (unsigned int)ntohl(key->val), + (unsigned int)ntohl(key->mask)); + } + } else { + fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,sel->nkeys); + } + + + fprintf(f, "\n "); + return 0; +} + +int +pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats) +{ + return 0; +} + +struct action_util pedit_action_util = { + .id = "pedit", + .parse_aopt = parse_pedit, + .print_aopt = print_pedit, +}; diff --git a/tc/m_pedit.h b/tc/m_pedit.h new file mode 100644 index 0000000..0a6d24e --- /dev/null +++ b/tc/m_pedit.h @@ -0,0 +1,62 @@ +/* + * m_pedit.h generic packet editor actions module + * + * 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 (hadi@cyberus.ca) + * + */ + +#ifndef _ACT_PEDIT_H_ +#define _ACT_PEDIT_H_ 1 + +#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_pedit.h> + +#define MAX_OFFS 128 + +#define TIPV4 1 +#define TIPV6 2 +#define TINT 3 +#define TU32 4 + +#define RU32 0xFFFFFFFF +#define RU16 0xFFFF +#define RU8 0xFF + +#define PEDITKINDSIZ 16 + +struct m_pedit_util +{ + struct m_pedit_util *next; + char id[PEDITKINDSIZ]; + int (*parse_peopt)(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +}; + + +extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int parse_val(int *argc_p, char ***argv_p, __u32 * val, int type); +extern int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +extern int parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey); +int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); +extern int print_pedit(struct action_util *au,FILE * f, struct rtattr *arg); +extern int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats); + +#endif diff --git a/tc/m_pedit.o b/tc/m_pedit.o new file mode 100644 index 0000000000000000000000000000000000000000..b3f95c8f542c5d219b8d18956ddd6852b86833ea GIT binary patch literal 13000 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 L6%>if`<VX&jc{G~ literal 0 HcmV?d00001 diff --git a/tc/m_police.c b/tc/m_police.c new file mode 100644 index 0000000..71adb59 --- /dev/null +++ b/tc/m_police.c @@ -0,0 +1,359 @@ +/* + * m_police.c Parse/print policing module options. + * + * This program is free software; you can u32istribute 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * FIXES: 19990619 - J Hadi Salim (hadi@cyberus.ca) + * simple addattr packaging fix. + * 2002: J Hadi Salim - Add tc action extensions syntax + * + */ + +#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" + +struct action_util police_action_util = { + .id = "police", + .parse_aopt = act_parse_police, + .print_aopt = print_police, +}; + +static void explain(void) +{ + fprintf(stderr, "Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n"); + fprintf(stderr, " [ peakrate BPS ] [ avrate BPS ]\n"); + fprintf(stderr, " [ ACTIONTERM ]\n"); + fprintf(stderr, "Old Syntax ACTIONTERM := action <EXCEEDACT>[/NOTEXCEEDACT] \n"); + fprintf(stderr, "New Syntax ACTIONTERM := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT] \n"); + fprintf(stderr, "Where: *EXCEEDACT := pipe | ok | reclassify | drop | continue \n"); + fprintf(stderr, "Where: pipe is only valid for new syntax \n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + + +char *police_action_n2a(int action, char *buf, int len) +{ + switch (action) { + case -1: + return "continue"; + break; + case TC_POLICE_OK: + return "pass"; + break; + case TC_POLICE_SHOT: + return "drop"; + break; + case TC_POLICE_RECLASSIFY: + return "reclassify"; + case TC_POLICE_PIPE: + return "pipe"; + default: + snprintf(buf, len, "%d", action); + return buf; + } +} + +int police_action_a2n(char *arg, int *result) +{ + int res; + + if (matches(arg, "continue") == 0) + res = -1; + else if (matches(arg, "drop") == 0) + res = TC_POLICE_SHOT; + else if (matches(arg, "shot") == 0) + res = TC_POLICE_SHOT; + else if (matches(arg, "pass") == 0) + res = TC_POLICE_OK; + else if (strcmp(arg, "ok") == 0) + res = TC_POLICE_OK; + else if (matches(arg, "reclassify") == 0) + res = TC_POLICE_RECLASSIFY; + else if (matches(arg, "pipe") == 0) + res = TC_POLICE_PIPE; + else { + char dummy; + if (sscanf(arg, "%d%c", &res, &dummy) != 1) + return -1; + } + *result = res; + return 0; +} + + +int get_police_result(int *action, int *result, char *arg) +{ + char *p = strchr(arg, '/'); + + if (p) + *p = 0; + + if (police_action_a2n(arg, action)) { + if (p) + *p = '/'; + return -1; + } + + if (p) { + *p = '/'; + if (police_action_a2n(p+1, result)) + return -1; + } + return 0; +} + + +int act_parse_police(struct action_util *a,int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + int argc = *argc_p; + char **argv = *argv_p; + int res = -1; + int ok=0; + struct tc_police p; + __u32 rtab[256]; + __u32 ptab[256]; + __u32 avrate = 0; + int presult = 0; + unsigned buffer=0, mtu=0, mpu=0; + int Rcell_log=-1, Pcell_log = -1; + struct rtattr *tail; + + memset(&p, 0, sizeof(p)); + p.action = TC_POLICE_RECLASSIFY; + + if (a) /* new way of doing things */ + NEXT_ARG(); + + if (argc <= 0) + return -1; + + while (argc > 0) { + + if (matches(*argv, "index") == 0) { + NEXT_ARG(); + if (get_u32(&p.index, *argv, 10)) { + fprintf(stderr, "Illegal \"index\"\n"); + return -1; + } + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (buffer) { + fprintf(stderr, "Double \"buffer/burst\" spec\n"); + return -1; + } + if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + } else if (strcmp(*argv, "mtu") == 0 || + strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (mtu) { + fprintf(stderr, "Double \"mtu/minburst\" spec\n"); + return -1; + } + if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { + explain1("mtu"); + return -1; + } + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (mpu) { + fprintf(stderr, "Double \"mpu\" spec\n"); + return -1; + } + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (p.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&p.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + } else if (strcmp(*argv, "avrate") == 0) { + NEXT_ARG(); + if (avrate) { + fprintf(stderr, "Double \"avrate\" spec\n"); + return -1; + } + if (get_rate(&avrate, *argv)) { + explain1("avrate"); + return -1; + } + } else if (matches(*argv, "peakrate") == 0) { + NEXT_ARG(); + if (p.peakrate.rate) { + fprintf(stderr, "Double \"peakrate\" spec\n"); + return -1; + } + if (get_rate(&p.peakrate.rate, *argv)) { + explain1("peakrate"); + return -1; + } + } else if (matches(*argv, "reclassify") == 0) { + p.action = TC_POLICE_RECLASSIFY; + } else if (matches(*argv, "drop") == 0 || + matches(*argv, "shot") == 0) { + p.action = TC_POLICE_SHOT; + } else if (matches(*argv, "continue") == 0) { + p.action = TC_POLICE_UNSPEC; + } else if (matches(*argv, "pass") == 0) { + p.action = TC_POLICE_OK; + } else if (matches(*argv, "pipe") == 0) { + p.action = TC_POLICE_PIPE; + } else if (strcmp(*argv, "conform-exceed") == 0) { + NEXT_ARG(); + if (get_police_result(&p.action, &presult, *argv)) { + fprintf(stderr, "Illegal \"action\"\n"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + break; + } + ok++; + argc--; argv++; + } + + if (!ok) + return -1; + + if (p.rate.rate && !buffer) { + fprintf(stderr, "\"burst\" requires \"rate\".\n"); + return -1; + } + if (p.peakrate.rate) { + if (!p.rate.rate) { + fprintf(stderr, "\"peakrate\" requires \"rate\".\n"); + return -1; + } + if (!mtu) { + fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); + return -1; + } + } + + if (p.rate.rate) { + if ((Rcell_log = tc_calc_rtable(p.rate.rate, rtab, Rcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate rate table.\n"); + return -1; + } + p.burst = tc_calc_xmittime(p.rate.rate, buffer); + p.rate.cell_log = Rcell_log; + p.rate.mpu = mpu; + } + p.mtu = mtu; + if (p.peakrate.rate) { + if ((Pcell_log = tc_calc_rtable(p.peakrate.rate, ptab, Pcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "POLICE: failed to calculate peak rate table.\n"); + return -1; + } + p.peakrate.cell_log = Pcell_log; + p.peakrate.mpu = mpu; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, MAX_MSG, tca_id, NULL, 0); + addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p, sizeof(p)); + if (p.rate.rate) + addattr_l(n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024); + if (p.peakrate.rate) + addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024); + if (avrate) + addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate); + if (presult) + addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult); + + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + res = 0; + + *argc_p = argc; + *argv_p = argv; + return res; +} + +int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) +{ + return act_parse_police(NULL,argc_p,argv_p,tca_id,n); +} + +int +print_police(struct action_util *a, FILE *f, struct rtattr *arg) +{ + SPRINT_BUF(b1); + struct tc_police *p; + struct rtattr *tb[TCA_POLICE_MAX+1]; + unsigned buffer; + + if (arg == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_POLICE_MAX, arg); + + if (tb[TCA_POLICE_TBF] == NULL) { + fprintf(f, "[NULL police tbf]"); + return 0; + } +#ifndef STOOPID_8BYTE + if (RTA_PAYLOAD(tb[TCA_POLICE_TBF]) < sizeof(*p)) { + fprintf(f, "[truncated police tbf]"); + return -1; + } +#endif + p = RTA_DATA(tb[TCA_POLICE_TBF]); + + fprintf(f, " police 0x%x ", p->index); + fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1)); + buffer = ((double)p->rate.rate*tc_core_tick2usec(p->burst))/1000000; + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + fprintf(f, "mtu %s ", sprint_size(p->mtu, b1)); + if (show_raw) + fprintf(f, "[%08x] ", p->burst); + if (p->peakrate.rate) + fprintf(f, "peakrate %s ", sprint_rate(p->peakrate.rate, b1)); + if (tb[TCA_POLICE_AVRATE]) + fprintf(f, "avrate %s ", sprint_rate(*(__u32*)RTA_DATA(tb[TCA_POLICE_AVRATE]), b1)); + fprintf(f, "action %s", police_action_n2a(p->action, b1, sizeof(b1))); + if (tb[TCA_POLICE_RESULT]) { + fprintf(f, "/%s ", police_action_n2a(*(int*)RTA_DATA(tb[TCA_POLICE_RESULT]), b1, sizeof(b1))); + } else + fprintf(f, " "); + fprintf(f, "\nref %d bind %d\n",p->refcnt, p->bindcnt); + + return 0; +} + +int +tc_print_police(FILE *f, struct rtattr *arg) { + return print_police(&police_action_util,f,arg); +} diff --git a/tc/m_police.o b/tc/m_police.o new file mode 100644 index 0000000000000000000000000000000000000000..aa6770750ca2bc7032a2ae1bb9c26811b94f64a2 GIT binary patch literal 12456 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ⅆ8f>D zMdw+e^Gm{~I=@!<KNkE`F8oUbUrOSiBfd|_JRcQ0^8~KW|3w6+^F&v4P6(X<;nVm- z1gAQkiq6|Y=MKWBI&p>np5Uil_yvW3PVm1*_;h{!1L4bedPpC9+i?o!hsJX+Sr<OA z$$9caqL1H#Brd<ZV;IPnMH3aok*^lS(YO3AiF*zfiQmUt(|skrQ{dDu>I?~76IaRK z?1BrJtycJ&Xm3;Sy`nv=;A7|r%9H$eI)3?G_oRZ$?|v5)yz;$Q=*jry_qYE8(T@)Y literal 0 HcmV?d00001 diff --git a/tc/p_icmp.c b/tc/p_icmp.c new file mode 100644 index 0000000..f9ddbe3 --- /dev/null +++ b/tc/p_icmp.c @@ -0,0 +1,61 @@ +/* + * m_pedit_icmp.c packet editor: ICMP header + * + * 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 (hadi@cyberus.ca) + * + */ + +#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 "m_pedit.h" + + +static int +parse_icmp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; +#if 0 + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "type") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, 0); + goto done; + } + if (strcmp(*argv, "code") == 0) { + NEXT_ARG(); + res = parse_u8(&argc, &argv, 1); + goto done; + } + return -1; + + done: + *argc_p = argc; + *argv_p = argv; +#endif + return res; +} + +struct m_pedit_util p_pedit_icmp = { + NULL, + "icmp", + parse_icmp, +}; diff --git a/tc/p_icmp.o b/tc/p_icmp.o new file mode 100644 index 0000000000000000000000000000000000000000..06cd4f45d49f1c4249d93520f6d6cea49477e6cb GIT binary patch literal 1416 zcmbu9&o9JK5XYzLHwO|D35RkJ5?;DiNF1bzunr<3k#JhoeL|>hw(r1+Kf#~k=<YA^ zAMh8f8J)4ssFRm8-<i+MyqWjf-N)U7y*|$aSsrXd&l3s|m0<LZBN&Gfc<thP>*f7r zT_<T)6`Nw{fp<SKJ~aHyeTdJM3I@e)?Cfj>OO3;m<)CiX&04S)hU>MkwizrP+g7k2 zyMnK7n9HQT3}Ety)WsL5u2blhSkB%%Fdcgvn^x>%Fc*2wzP)N*cH-26xobOXu-|I# z;^YQQ(oR!*=VYFL>MYjNk$=v|`$eppPcRo*M1$&m0j%e&0jr4i=P}SP!IGX16wVB@ zQPe(shS?Xs!c6yn;yf+7*9Iog{Iqv@J+Tv(A==Vka19N;C&jXg`3XtA%pW16{(8M= zz`o8sLarI5N<Gb2UOIOYgZl0$|DB7@SVeR_`TFiCH{whA;KDJ~5q$LdiB<Iv`TiET zUPSj7u9foo;j^nb`F`b%5wAK4@!xN__#S)Hlz%5a@bId+4?~I+uK#l@z3?=elcjkA i31v<CD>DgXaVNLm4rI;Dwys^_6p8YE>Gvc1X#NGfs!R<4 literal 0 HcmV?d00001 diff --git a/tc/p_ip.c b/tc/p_ip.c new file mode 100644 index 0000000..1b2a440 --- /dev/null +++ b/tc/p_ip.c @@ -0,0 +1,159 @@ +/* + * m_pedit.c packet editor: IPV4/6 header + * + * 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 (hadi@cyberus.ca) + * + */ + +#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 "m_pedit.h" + +static int +parse_ip(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + int argc = *argc_p; + char **argv = *argv_p; + + if (argc < 2) + return -1; + + if (strcmp(*argv, "src") == 0) { + NEXT_ARG(); + tkey->off = 12; + res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + goto done; + } + if (strcmp(*argv, "dst") == 0) { + NEXT_ARG(); + tkey->off = 16; + res = parse_cmd(&argc, &argv, 4, TIPV4,RU32,sel,tkey); + goto done; + } + /* jamal - look at these and make them either old or new + ** scheme given diffserv + ** dont forget the CE bit + */ + if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { + NEXT_ARG(); + tkey->off = 1; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "ihl") == 0) { + NEXT_ARG(); + tkey->off = 0; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "protocol") == 0) { + NEXT_ARG(); + tkey->off = 9; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + /* jamal - fix this */ + if (matches(*argv, "precedence") == 0) { + NEXT_ARG(); + tkey->off = 1; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + /* jamal - validate this at some point */ + if (strcmp(*argv, "nofrag") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x3F,sel,tkey); + goto done; + } + /* jamal - validate this at some point */ + if (strcmp(*argv, "firstfrag") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x1F,sel,tkey); + goto done; + } + if (strcmp(*argv, "ce") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x80,sel,tkey); + goto done; + } + if (strcmp(*argv, "df") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x40,sel,tkey); + goto done; + } + if (strcmp(*argv, "mf") == 0) { + NEXT_ARG(); + tkey->off = 6; + res = parse_cmd(&argc, &argv, 1, TU32,0x20,sel,tkey); + goto done; + } + if (strcmp(*argv, "dport") == 0) { + NEXT_ARG(); + tkey->off = 22; + res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + goto done; + } + if (strcmp(*argv, "sport") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 2, TU32,RU16,sel,tkey); + goto done; + } + if (strcmp(*argv, "icmp_type") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + if (strcmp(*argv, "icmp_code") == 0) { + NEXT_ARG(); + tkey->off = 20; + res = parse_cmd(&argc, &argv, 1, TU32,RU8,sel,tkey); + goto done; + } + return -1; + + done: + *argc_p = argc; + *argv_p = argv; + return res; +} + +static int +parse_ip6(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} + +struct m_pedit_util p_pedit_ip = { + NULL, + "ip", + parse_ip, +}; + + +struct m_pedit_util p_pedit_ip6 = { + NULL, + "ip6", + parse_ip6, +}; diff --git a/tc/p_ip.o b/tc/p_ip.o new file mode 100644 index 0000000000000000000000000000000000000000..3479e0b026e7197dcfa8ef525d3eabf583614699 GIT binary patch literal 3576 zcmbtWU1%It6uz5H?AmTJsgzQ%*agQnMY80Nn2OYmY3yv-2o<Xmi*Y+MYqstmyR+Cr zse$c^!<rC%@aM^gf<F)Sp%1aiLy4`J20`$lefBX+6e1!hM%VA$xi=Z^ZrTSA%-(a( zJ?A?=_spGrXFTzITOhzB1=w-6ElDk7p<Ua2jI%M;#r816J*)q0xEX!TaDPa+mcEgI z@TXDvWu&GhYF&nVcc}r2yMM&pP4`~quYjA-!wLL!U(Up9d$onO%38^}R9)7pp8>eB z8n1jCs;+ViGS%jOMM5iMp)ht0_n;nk>!UXabCD8VYA5xqqjwv#m-O%&B<9|#ozTPX zy-~MaH^6qs-En5r&gut@T1Jn!8;P2w_awmg8I|uwZfgtA1uAPrt@<`lAs{1DtDg6S zATqm>0-s9Fr{-nGR=LyjDrr~d5wf~m)?1`L;HBOTi+T(gTmFtS4wpRD$}4TGR#&}h z8?Z&SRNHp75EiM>B~sTrc{WHVHSZ;nFJJf4E|tFm6I{RQiH=q^MHTf)F!9>4n*d<E zOVwp(B2mLR$ePmS{T|#jFAiPEl-mgonfAjl+?4I*Gsu(x>!*QECLFN-1f+>t;0A3i zK8&AD!(B<Z-~ax3xu-Qy7(irL55bkE#}iGsD<apjW>j#JA#$((_-~`ZBY6>MHYox^ zdO`YH2l;}J&a<#aw1==zgX>oT=Hcvy3IGI!Oov*6XmhVqBWVT?DL|6|8r2pUrl+id ziD9VhatCp3XyHzHT@r6b!P^Zyc{W?~mf}ZpKfIgdM)}1QyrqA(@v?G$AB>lico7Bf zJn-ZxFHt+GafbVocLv9IXx|~gq5b<|FWzK1+ig(s6Y1R)C&E$1i2xXfqVIsZid=xY z4-o5L^3z=Mou=n|MDeBft-7ks&2IbF-L257`%N5EL;Lua_Tlo#Ev<U(Z!~juG-SA# zO1f?W8`Wj-;q|XmbW@0jUsSh|)dMJ97{0GXy3l&tDy7nP#$ttH-pQN!3>e$AEjwr0 zESFCelW($Ax>#~JXBF~Ahn4tm+RPTFoQnmUan8(JHbtNY5(QY`LU(vqXr3+{kdEgd z=e(bN9o~BC-S$ey>8{vp+xtP%kpBs9i~rJCa5_0Q_H?A@<O^qxMh2rp(SgW7U*AxF zU;k5)o)>K^Vk8|;-#Z*lnS)0ei<T~Co#Z*JPSIPZC0DdF$*5yra9EVHR?<nb=($n} za-Ib8>5uk{ZF_nO<!5ac&E*|CiX3`NPSSjfMa_IRYv&x9<NsJA@TFF5)hn=KVm&PS zB}7JF1V23rfi8HJ-k`4@XhfU?iNx|-7wZ8$L{H)&JwF6e2$~UbV^Sg3h$0_`+$#Pj zot|Nzu&4MGLyg}rbZY!%rEUS!6pN;x@*j`}VwL_Aie9vX`_R@YJfx#L#82JgKwkAL z!OQ%mdK^rv`tP);pFTIzU-thc=wwb3lh$c<AjGC>YeBE-{QlHmKdH8Rv8nc+>^~Lp zvwmmrsz1qU@5tpnx&}cr!hzbc2aj4meY;!5=l|NU_X-|Asqy`LexB^w*>CixsELA2 zRd^=BmYGm~&BI7_v;ij~w&Za=BJ?t-yzfX3HofJZRgn4qBuhtbPu*#zAQR2?ZP7ij z*Oz}p>>Uw$Da)1K7xgZIiaoP~{=^P?R+vf`q9!XOizOTE@D_)if^DT8Pr1d5eKTvZ zY|=5O?Gj7paGw@3wqsAx#hT1vs6U`&Kr~x4JNUeEx-JHQI^gfxH$qSLT|WEA#BfS` zIe#w;JuRBg{x!v(-VY+vXYa?KQS9Zn?vA2=+=F55;<AwW^a}l?q8}9cj}-kep}(u> d$An(qE7T53LC)uh*q3@Cvd9j4L(xl|{{U0;!Y}{; literal 0 HcmV?d00001 diff --git a/tc/p_tcp.c b/tc/p_tcp.c new file mode 100644 index 0000000..aab37a6 --- /dev/null +++ b/tc/p_tcp.c @@ -0,0 +1,38 @@ +/* + * m_pedit_tcp.c packet editor: TCP header + * + * 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 (hadi@cyberus.ca) + * + */ + +#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 "m_pedit.h" + +static int +parse_tcp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} +struct m_pedit_util p_pedit_tcp = { + NULL, + "tcp", + parse_tcp, +}; + + diff --git a/tc/p_tcp.o b/tc/p_tcp.o new file mode 100644 index 0000000000000000000000000000000000000000..eecab5eacdeea0584120d7c90c65270e68a531f9 GIT binary patch literal 1408 zcmbu9%}N7749BxwzxAN#K~QkLh=LAVl?onYLHsy~h~U9fUAH3@ZC$!^s3&~{AH}10 zpTsxNC(y(uc8KfAf#sk4l1wtQ+dk|a?N@>TWCgGTeNQMr;=$w_Coln{@bZc4wV(Hw z<+8jqG{ZE2pgTD+Jo3bSh|hZ!Lt-E7?rulR2gheC(T3SH>(P1~Z&u^#R<wL-8_{9v zdVH;BuCk3408`wzTzZM>Iz4TQ<!sl1>Db%UG*Xv>xhx9yZL@yWNm~|7`_5T|{niSX zW;bB6yH?A#oy_x3ox^%M^3VBrzl2ruN#-I;Xi$A9fc1hkVinQ;A_fK|=;_H|&zWU5 zhFZaAgni+w%yj=p&eNiM)i8nPr@j64$WH7-w57k`DjIrEiZx5-CnWVUe}atq>-DSw z`#Sdoxn`6q^)#QqbnX-e_1#ha8yB6kis*Xs_1#f!!k6;Sh2yBB_~`Q!E9)Qg{Vj04 zi0&_3E9DKq$EW7xhov`0yzC^zf4^axyW;KR|4u^S;Z^exh85|#;m@t~!_#O^dGkaP f%Io-7W-`drPGQN0yq?>}wfnX~zAOD+q+atc{f|m- literal 0 HcmV?d00001 diff --git a/tc/p_udp.c b/tc/p_udp.c new file mode 100644 index 0000000..95ed993 --- /dev/null +++ b/tc/p_udp.c @@ -0,0 +1,38 @@ +/* + * m_pedit_udp.c packet editor: UDP header + * + * 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 (hadi@cyberus.ca) + * + */ + +#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 "m_pedit.h" + +static int +parse_udp(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) +{ + int res = -1; + return res; +} + +struct m_pedit_util p_pedit_udp = { + NULL, + "udp", + parse_udp, +}; + diff --git a/tc/p_udp.o b/tc/p_udp.o new file mode 100644 index 0000000000000000000000000000000000000000..61232d9f42fb79ed84aaa5fabca167602e679913 GIT binary patch literal 1408 zcmbu9%}N7749BxwzxAN#K~QkLh=Luqttfbq1@Yq`B7z4`b#13qw7PUh(33ubkK)n0 zPvRTs6KG-+JH++m!17OiNhX=uZ69_Hc1uA3vI5wGz9$qQ@nG_e6PSQec=^Qj+RyvT zx@~5Kp&6zD1b34Y!y`}Jhxoi#F(mfh_V#A9ymxrA64lI_sYcZ}UaQ2FjcEDUHlzL2 z^>}&Rv>LS)0F&Q$TzZb`x;<@+<?NjU)3sNrX{Ig(bDrny+jhOxO*<CM^^LOz`;~K- zHZH+5ZaN)%?PQ*R>KxY7k$=v|`z5TJPcj!-LWAl<0jy`N5vz#y7cnp(K~Ikcd(JGg zG1L-1BkT)bWv2T-a-J65YaJ74e%jk#kL<)gL|gg`uArg!q*%LPenL_&^C!rtzh2K8 zu&;BEkZVS%Qcv^wOXp5uP~RQpzj4titB9^AU*8?&CVVOHTsV$8ijO`&v7-Jl-`@h) zi|GEswNl;ye0*w7epq;8#EVWs{P!ECxl7(Y{_i9N9$qyMVOWu#8~)r%KRk`*ls8W# gp{$O7Wu^gH+RZK5kkvEWyl~$($akgRi_~lW1qo0}cK`qY literal 0 HcmV?d00001 diff --git a/tc/q_atm.c b/tc/q_atm.c new file mode 100644 index 0000000..4c8dc0b --- /dev/null +++ b/tc/q_atm.c @@ -0,0 +1,260 @@ +/* + * q_atm.c ATM. + * + * Hacked 1998-2000 by Werner Almesberger, EPFL ICA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <atm.h> +#include <linux/atmdev.h> +#include <linux/atmarp.h> + +#include "utils.h" +#include "tc_util.h" + + +#define MAX_HDR_LEN 64 + +#define usage() return(-1) + + +static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + if (argc) { + fprintf(stderr,"Usage: atm\n"); + return -1; + } + return 0; +} + + +static void explain(void) +{ + fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) " + "[ qos QOS ] [ sndbuf BYTES ]\n"); + fprintf(stderr, " [ hdr HEX... ] [ excess ( CLASSID | clp ) ] " + "[ clip ]\n"); +} + + +static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct sockaddr_atmsvc addr; + struct atm_qos qos; + struct atm_sap sap; + unsigned char hdr[MAX_HDR_LEN]; + __u32 excess = 0; + struct rtattr *tail; + int sndbuf = 0; + int hdr_len = -1; + int set_clip = 0; + int s; + + memset(&addr,0,sizeof(addr)); + (void) text2qos("aal5,ubr:sdu=9180,rx:none",&qos,0); + (void) text2sap("blli:l2=iso8802",&sap,0); + while (argc > 0) { + if (!strcmp(*argv,"pvc")) { + NEXT_ARG(); + if (text2atm(*argv,(struct sockaddr *) &addr, + sizeof(addr),T2A_PVC | T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"svc")) { + NEXT_ARG(); + if (text2atm(*argv,(struct sockaddr *) &addr, + sizeof(addr),T2A_SVC | T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"qos")) { + NEXT_ARG(); + if (text2qos(*argv,&qos,0) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"sndbuf")) { + char *end; + + NEXT_ARG(); + sndbuf = strtol(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"sap")) { + NEXT_ARG(); + if (addr.sas_family != AF_ATMSVC || + text2sap(*argv,&sap,T2A_NAME) < 0) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"hdr")) { + unsigned char *ptr; + char *walk; + + NEXT_ARG(); + ptr = hdr; + for (walk = *argv; *walk; walk++) { + int tmp; + + if (ptr == hdr+MAX_HDR_LEN) { + fprintf(stderr,"header is too long\n"); + return -1; + } + if (*walk == '.') continue; + if (!isxdigit(walk[0]) || !walk[1] || + !isxdigit(walk[1])) { + explain(); + return -1; + } + sscanf(walk,"%2x",&tmp); + *ptr++ = tmp; + walk++; + } + hdr_len = ptr-hdr; + } + else if (!strcmp(*argv,"excess")) { + NEXT_ARG(); + if (!strcmp(*argv,"clp")) excess = 0; + else if (get_tc_classid(&excess,*argv)) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"clip")) { + set_clip = 1; + } + else { + explain(); + return 1; + } + argc--; + argv++; + } + s = socket(addr.sas_family,SOCK_DGRAM,0); + if (s < 0) { + perror("socket"); + return -1; + } + if (setsockopt(s,SOL_ATM,SO_ATMQOS,&qos,sizeof(qos)) < 0) { + perror("SO_ATMQOS"); + return -1; + } + if (sndbuf) + if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + if (addr.sas_family == AF_ATMSVC && setsockopt(s,SOL_ATM,SO_ATMSAP, + &sap,sizeof(sap)) < 0) { + perror("SO_ATMSAP"); + return -1; + } + if (connect(s,(struct sockaddr *) &addr,addr.sas_family == AF_ATMPVC ? + sizeof(struct sockaddr_atmpvc) : sizeof(addr)) < 0) { + perror("connect"); + return -1; + } + if (set_clip) + if (ioctl(s,ATMARP_MKIP,0) < 0) { + perror("ioctl ATMARP_MKIP"); + return -1; + } + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + addattr_l(n,1024,TCA_ATM_FD,&s,sizeof(s)); + if (excess) addattr_l(n,1024,TCA_ATM_EXCESS,&excess,sizeof(excess)); + if (hdr_len != -1) addattr_l(n,1024,TCA_ATM_HDR,hdr,hdr_len); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + + +static int atm_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_ATM_MAX+1]; + char buffer[MAX_ATM_ADDR_LEN+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ATM_MAX, opt); + if (tb[TCA_ATM_ADDR]) { + if (RTA_PAYLOAD(tb[TCA_ATM_ADDR]) < + sizeof(struct sockaddr_atmpvc)) + fprintf(stderr,"ATM: address too short\n"); + else { + if (atm2text(buffer,MAX_ATM_ADDR_LEN, + RTA_DATA(tb[TCA_ATM_ADDR]),A2T_PRETTY | A2T_NAME) < + 0) fprintf(stderr,"atm2text error\n"); + fprintf(f,"pvc %s ",buffer); + } + } + if (tb[TCA_ATM_HDR]) { + int i; + + fprintf(f,"hdr"); + for (i = 0; i < RTA_PAYLOAD(tb[TCA_ATM_HDR]); i++) + fprintf(f,"%c%02x",i ? '.' : ' ', + ((unsigned char *) RTA_DATA(tb[TCA_ATM_HDR]))[i]); + if (!i) fprintf(f," ."); + fprintf(f," "); + } + if (tb[TCA_ATM_EXCESS]) { + __u32 excess; + + if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS]) < sizeof(excess)) + fprintf(stderr,"ATM: excess class ID too short\n"); + else { + excess = *(__u32 *) RTA_DATA(tb[TCA_ATM_EXCESS]); + if (!excess) fprintf(f,"excess clp "); + else { + char buf[64]; + + print_tc_classid(buf,sizeof(buf),excess); + fprintf(f,"excess %s ",buf); + } + } + } + if (tb[TCA_ATM_STATE]) { + static const char *map[] = { ATM_VS2TXT_MAP }; + int state; + + if (RTA_PAYLOAD(tb[TCA_ATM_STATE]) < sizeof(state)) + fprintf(stderr,"ATM: state field too short\n"); + else { + state = *(int *) RTA_DATA(tb[TCA_ATM_STATE]); + fprintf(f,"%s ",map[state]); + } + } + return 0; +} + + +struct qdisc_util atm_qdisc_util = { + .id = "atm", + .parse_qopt = atm_parse_opt, + .print_qopt = atm_print_opt, + .parse_copt = atm_parse_class_opt, + .print_copt = atm_print_opt, +}; diff --git a/tc/q_cbq.c b/tc/q_cbq.c new file mode 100644 index 0000000..40c0228 --- /dev/null +++ b/tc/q_cbq.c @@ -0,0 +1,553 @@ +/* + * q_cbq.c CBQ. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 "tc_cbq.h" + +static void explain_class(void) +{ + fprintf(stderr, "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n"); + fprintf(stderr, " [ minburst PKTS ] [ bounded ] [ isolated ]\n"); + fprintf(stderr, " [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n"); + fprintf(stderr, " [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ split CLASSID ] [ defmap MASK/CHANGE ]\n"); +} + +static void explain(void) +{ + fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n"); + fprintf(stderr, " [ cell BYTES ] [ ewma LOG ]\n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + +static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_ratespec r; + struct tc_cbq_lssopt lss; + __u32 rtab[256]; + unsigned mpu=0, avpkt=0, allot=0; + int cell_log=-1; + int ewma_log=-1; + struct rtattr *tail; + + memset(&lss, 0, sizeof(lss)); + memset(&r, 0, sizeof(r)); + + while (argc > 0) { + if (strcmp(*argv, "bandwidth") == 0 || + strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&r.rate, *argv)) { + explain1("bandwidth"); + return -1; + } + } else if (strcmp(*argv, "ewma") == 0) { + NEXT_ARG(); + if (get_unsigned(&ewma_log, *argv, 0)) { + explain1("ewma"); + return -1; + } + if (ewma_log > 31) { + fprintf(stderr, "ewma_log must be < 32\n"); + return -1; + } + } else if (strcmp(*argv, "cell") == 0) { + unsigned cell; + int i; + NEXT_ARG(); + if (get_size(&cell, *argv)) { + explain1("cell"); + return -1; + } + for (i=0; i<32; i++) + if ((1<<i) == cell) + break; + if (i>=32) { + fprintf(stderr, "cell must be 2^n\n"); + return -1; + } + cell_log = i; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + explain1("avpkt"); + return -1; + } + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "allot") == 0) { + NEXT_ARG(); + /* Accept and ignore "allot" for backward compatibility */ + if (get_size(&allot, *argv)) { + explain1("allot"); + return -1; + } + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + /* OK. All options are parsed. */ + + if (r.rate == 0) { + fprintf(stderr, "CBQ: bandwidth is required parameter.\n"); + return -1; + } + if (avpkt == 0) { + fprintf(stderr, "CBQ: \"avpkt\" is required.\n"); + return -1; + } + if (allot < (avpkt*3)/2) + allot = (avpkt*3)/2; + + if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, allot, mpu)) < 0) { + fprintf(stderr, "CBQ: failed to calculate rate table.\n"); + return -1; + } + r.cell_log = cell_log; + r.mpu = mpu; + + if (ewma_log < 0) + ewma_log = TC_CBQ_DEF_EWMA; + lss.ewma_log = ewma_log; + lss.maxidle = tc_cbq_calc_maxidle(r.rate, r.rate, avpkt, lss.ewma_log, 0); + lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + lss.avpkt = avpkt; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r)); + addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss)); + addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); + if (show_raw) { + int i; + for (i=0; i<256; i++) + printf("%u ", rtab[i]); + printf("\n"); + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int wrr_ok=0, fopt_ok=0; + struct tc_ratespec r; + struct tc_cbq_lssopt lss; + struct tc_cbq_wrropt wrr; + struct tc_cbq_fopt fopt; + struct tc_cbq_ovl ovl; + __u32 rtab[256]; + unsigned mpu=0; + int cell_log=-1; + int ewma_log=-1; + unsigned bndw = 0; + unsigned minburst=0, maxburst=0; + struct rtattr *tail; + + memset(&r, 0, sizeof(r)); + memset(&lss, 0, sizeof(lss)); + memset(&wrr, 0, sizeof(wrr)); + memset(&fopt, 0, sizeof(fopt)); + memset(&ovl, 0, sizeof(ovl)); + + while (argc > 0) { + if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&r.rate, *argv)) { + explain1("rate"); + return -1; + } + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&bndw, *argv)) { + explain1("bandwidth"); + return -1; + } + } else if (strcmp(*argv, "minidle") == 0) { + NEXT_ARG(); + if (get_u32(&lss.minidle, *argv, 0)) { + explain1("minidle"); + return -1; + } + lss.change |= TCF_CBQ_LSS_MINIDLE; + } else if (strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (get_u32(&minburst, *argv, 0)) { + explain1("minburst"); + return -1; + } + lss.change |= TCF_CBQ_LSS_OFFTIME; + } else if (strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (get_u32(&maxburst, *argv, 0)) { + explain1("maxburst"); + return -1; + } + lss.change |= TCF_CBQ_LSS_MAXIDLE; + } else if (strcmp(*argv, "bounded") == 0) { + lss.flags |= TCF_CBQ_LSS_BOUNDED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "borrow") == 0) { + lss.flags &= ~TCF_CBQ_LSS_BOUNDED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "isolated") == 0) { + lss.flags |= TCF_CBQ_LSS_ISOLATED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "sharing") == 0) { + lss.flags &= ~TCF_CBQ_LSS_ISOLATED; + lss.change |= TCF_CBQ_LSS_FLAGS; + } else if (strcmp(*argv, "ewma") == 0) { + NEXT_ARG(); + if (get_u32(&ewma_log, *argv, 0)) { + explain1("ewma"); + return -1; + } + if (ewma_log > 31) { + fprintf(stderr, "ewma_log must be < 32\n"); + return -1; + } + lss.change |= TCF_CBQ_LSS_EWMA; + } else if (strcmp(*argv, "cell") == 0) { + unsigned cell; + int i; + NEXT_ARG(); + if (get_size(&cell, *argv)) { + explain1("cell"); + return -1; + } + for (i=0; i<32; i++) + if ((1<<i) == cell) + break; + if (i>=32) { + fprintf(stderr, "cell must be 2^n\n"); + return -1; + } + cell_log = i; + } else if (strcmp(*argv, "prio") == 0) { + unsigned prio; + NEXT_ARG(); + if (get_u32(&prio, *argv, 0)) { + explain1("prio"); + return -1; + } + if (prio > TC_CBQ_MAXPRIO) { + fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO); + return -1; + } + wrr.priority = prio; + wrr_ok++; + } else if (strcmp(*argv, "allot") == 0) { + NEXT_ARG(); + if (get_size(&wrr.allot, *argv)) { + explain1("allot"); + return -1; + } + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&lss.avpkt, *argv)) { + explain1("avpkt"); + return -1; + } + lss.change |= TCF_CBQ_LSS_AVPKT; + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + } else if (strcmp(*argv, "weight") == 0) { + NEXT_ARG(); + if (get_size(&wrr.weight, *argv)) { + explain1("weight"); + return -1; + } + wrr_ok++; + } else if (strcmp(*argv, "split") == 0) { + NEXT_ARG(); + if (get_tc_classid(&fopt.split, *argv)) { + fprintf(stderr, "Invalid split node ID.\n"); + usage(); + } + fopt_ok++; + } else if (strcmp(*argv, "defmap") == 0) { + int err; + NEXT_ARG(); + err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange); + if (err < 1) { + fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n"); + return -1; + } + if (err == 1) + fopt.defchange = ~0; + fopt_ok++; + } else if (strcmp(*argv, "help") == 0) { + explain_class(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain_class(); + return -1; + } + argc--; argv++; + } + + /* OK. All options are parsed. */ + + /* 1. Prepare link sharing scheduler parameters */ + if (r.rate) { + unsigned pktsize = wrr.allot; + if (wrr.allot < (lss.avpkt*3)/2) + wrr.allot = (lss.avpkt*3)/2; + if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, pktsize, mpu)) < 0) { + fprintf(stderr, "CBQ: failed to calculate rate table.\n"); + return -1; + } + r.cell_log = cell_log; + r.mpu = mpu; + } + if (ewma_log < 0) + ewma_log = TC_CBQ_DEF_EWMA; + lss.ewma_log = ewma_log; + if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) { + if (lss.avpkt == 0) { + fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n"); + return -1; + } + if (bndw==0 || r.rate == 0) { + fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n"); + return -1; + } + } + if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) { + wrr_ok = 1; + wrr.priority = TC_CBQ_MAXPRIO; + if (wrr.allot == 0) + wrr.allot = (lss.avpkt*3)/2; + } + if (wrr_ok) { + if (wrr.weight == 0) + wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate; + if (wrr.allot == 0) { + fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n"); + return -1; + } + } + if (lss.change&TCF_CBQ_LSS_MAXIDLE) { + lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst); + lss.change |= TCF_CBQ_LSS_MAXIDLE; + lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + } + if (lss.change&TCF_CBQ_LSS_OFFTIME) { + lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst); + lss.change |= TCF_CBQ_LSS_OFFTIME; + lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT; + } + if (lss.change&TCF_CBQ_LSS_MINIDLE) { + lss.minidle <<= lss.ewma_log; + lss.change |= TCF_CBQ_LSS_EWMA; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + if (lss.change) { + lss.change |= TCF_CBQ_LSS_FLAGS; + addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss)); + } + if (wrr_ok) + addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr)); + if (fopt_ok) + addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt)); + if (r.rate) { + addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r)); + addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024); + if (show_raw) { + int i; + for (i=0; i<256; i++) + printf("%u ", rtab[i]); + printf("\n"); + } + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_CBQ_MAX+1]; + struct tc_ratespec *r = NULL; + struct tc_cbq_lssopt *lss = NULL; + struct tc_cbq_wrropt *wrr = NULL; + struct tc_cbq_fopt *fopt = NULL; + struct tc_cbq_ovl *ovl = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_CBQ_MAX, opt); + + if (tb[TCA_CBQ_RATE]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r)) + fprintf(stderr, "CBQ: too short rate opt\n"); + else + r = RTA_DATA(tb[TCA_CBQ_RATE]); + } + if (tb[TCA_CBQ_LSSOPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss)) + fprintf(stderr, "CBQ: too short lss opt\n"); + else + lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]); + } + if (tb[TCA_CBQ_WRROPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr)) + fprintf(stderr, "CBQ: too short wrr opt\n"); + else + wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]); + } + if (tb[TCA_CBQ_FOPT]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt)) + fprintf(stderr, "CBQ: too short fopt\n"); + else + fopt = RTA_DATA(tb[TCA_CBQ_FOPT]); + } + if (tb[TCA_CBQ_OVL_STRATEGY]) { + if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl)) + fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n", + (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), + (unsigned) sizeof(*ovl)); + else + ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]); + } + + if (r) { + char buf[64]; + print_rate(buf, sizeof(buf), r->rate); + fprintf(f, "rate %s ", buf); + if (show_details) { + fprintf(f, "cell %ub ", 1<<r->cell_log); + if (r->mpu) + fprintf(f, "mpu %ub ", r->mpu); + } + } + if (lss && lss->flags) { + int comma=0; + fprintf(f, "("); + if (lss->flags&TCF_CBQ_LSS_BOUNDED) { + fprintf(f, "bounded"); + comma=1; + } + if (lss->flags&TCF_CBQ_LSS_ISOLATED) { + if (comma) + fprintf(f, ","); + fprintf(f, "isolated"); + } + fprintf(f, ") "); + } + if (wrr) { + if (wrr->priority != TC_CBQ_MAXPRIO) + fprintf(f, "prio %u", wrr->priority); + else + fprintf(f, "prio no-transmit"); + if (show_details) { + char buf[64]; + fprintf(f, "/%u ", wrr->cpriority); + if (wrr->weight != 1) { + print_rate(buf, sizeof(buf), wrr->weight); + fprintf(f, "weight %s ", buf); + } + if (wrr->allot) + fprintf(f, "allot %ub ", wrr->allot); + } + } + if (lss && show_details) { + fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt); + if (lss->maxidle) { + fprintf(f, "maxidle %luus ", tc_core_tick2usec(lss->maxidle>>lss->ewma_log)); + if (show_raw) + fprintf(f, "[%08x] ", lss->maxidle); + } + if (lss->minidle!=0x7fffffff) { + fprintf(f, "minidle %luus ", tc_core_tick2usec(lss->minidle>>lss->ewma_log)); + if (show_raw) + fprintf(f, "[%08x] ", lss->minidle); + } + if (lss->offtime) { + fprintf(f, "offtime %luus ", tc_core_tick2usec(lss->offtime)); + if (show_raw) + fprintf(f, "[%08x] ", lss->offtime); + } + } + if (fopt && show_details) { + char buf[64]; + print_tc_classid(buf, sizeof(buf), fopt->split); + fprintf(f, "\nsplit %s ", buf); + if (fopt->defmap) { + fprintf(f, "defmap %08x", fopt->defmap); + } + } + return 0; +} + +static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_cbq_xstats *st; + + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " borrowed %u overactions %u avgidle %g undertime %g", st->borrows, + st->overactions, (double)st->avgidle, (double)st->undertime); + return 0; +} + +struct qdisc_util cbq_qdisc_util = { + .id = "cbq", + .parse_qopt = cbq_parse_opt, + .print_qopt = cbq_print_opt, + .print_xstats = cbq_print_xstats, + .parse_copt = cbq_parse_class_opt, + .print_copt = cbq_print_opt, +}; + diff --git a/tc/q_cbq.o b/tc/q_cbq.o new file mode 100644 index 0000000000000000000000000000000000000000..3f043d05281ac8ae9739d5a302c107c80a5dd041 GIT binary patch literal 16232 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 literal 0 HcmV?d00001 diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c new file mode 100644 index 0000000..384e749 --- /dev/null +++ b/tc/q_dsmark.c @@ -0,0 +1,179 @@ +/* + * q_dsmark.c Differentiated Services field marking. + * + * Hacked 1998,1999 by Werner Almesberger, EPFL ICA + * + */ + +#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" + + +#define usage() return(-1) + + +static void explain(void) +{ + fprintf(stderr,"Usage: dsmark indices INDICES [ default_index " + "DEFAULT_INDEX ] [ set_tc_index ]\n"); +} + + +static int dsmark_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct rtattr *tail; + __u16 ind; + char *end; + int dflt,set_tc_index; + + ind = set_tc_index = 0; + dflt = -1; + while (argc > 0) { + if (!strcmp(*argv,"indices")) { + NEXT_ARG(); + ind = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"default_index") || !strcmp(*argv, + "default")) { + NEXT_ARG(); + dflt = strtoul(*argv,&end,0); + if (*end) { + explain(); + return -1; + } + } + else if (!strcmp(*argv,"set_tc_index")) { + set_tc_index = 1; + } + else { + explain(); + return -1; + } + argc--; + argv++; + } + if (!ind) { + explain(); + return -1; + } + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + addattr_l(n,1024,TCA_DSMARK_INDICES,&ind,sizeof(ind)); + if (dflt != -1) { + __u16 tmp = dflt; + + addattr_l(n,1024,TCA_DSMARK_DEFAULT_INDEX,&tmp,sizeof(tmp)); + } + if (set_tc_index) addattr_l(n,1024,TCA_DSMARK_SET_TC_INDEX,NULL,0); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + +static void explain_class(void) +{ + fprintf(stderr, "Usage: ... dsmark [ mask MASK ] [ value VALUE ]\n"); +} + + +static int dsmark_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct rtattr *tail; + __u8 tmp; + char *end; + + tail = NLMSG_TAIL(n); + addattr_l(n,1024,TCA_OPTIONS,NULL,0); + while (argc > 0) { + if (!strcmp(*argv,"mask")) { + NEXT_ARG(); + tmp = strtoul(*argv,&end,0); + if (*end) { + explain_class(); + return -1; + } + addattr_l(n,1024,TCA_DSMARK_MASK,&tmp,1); + } + else if (!strcmp(*argv,"value")) { + NEXT_ARG(); + tmp = strtoul(*argv,&end,0); + if (*end) { + explain_class(); + return -1; + } + addattr_l(n,1024,TCA_DSMARK_VALUE,&tmp,1); + } + else { + explain_class(); + return -1; + } + argc--; + argv++; + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + + + +static int dsmark_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_DSMARK_MAX+1]; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_DSMARK_MAX, opt); + + if (tb[TCA_DSMARK_MASK]) { + if (!RTA_PAYLOAD(tb[TCA_DSMARK_MASK])) + fprintf(stderr,"dsmark: empty mask\n"); + else fprintf(f,"mask 0x%02x ", + *(__u8 *) RTA_DATA(tb[TCA_DSMARK_MASK])); + } + if (tb[TCA_DSMARK_VALUE]) { + if (!RTA_PAYLOAD(tb[TCA_DSMARK_VALUE])) + fprintf(stderr,"dsmark: empty value\n"); + else fprintf(f,"value 0x%02x ", + *(__u8 *) RTA_DATA(tb[TCA_DSMARK_VALUE])); + } + if (tb[TCA_DSMARK_INDICES]) { + if (RTA_PAYLOAD(tb[TCA_DSMARK_INDICES]) < sizeof(__u16)) + fprintf(stderr,"dsmark: indices too short\n"); + else fprintf(f,"indices 0x%04x ", + *(__u16 *) RTA_DATA(tb[TCA_DSMARK_INDICES])); + } + if (tb[TCA_DSMARK_DEFAULT_INDEX]) { + if (RTA_PAYLOAD(tb[TCA_DSMARK_DEFAULT_INDEX]) < sizeof(__u16)) + fprintf(stderr,"dsmark: default_index too short\n"); + else fprintf(f,"default_index 0x%04x ", + *(__u16 *) RTA_DATA(tb[TCA_DSMARK_DEFAULT_INDEX])); + } + if (tb[TCA_DSMARK_SET_TC_INDEX]) fprintf(f,"set_tc_index "); + return 0; +} + + +struct qdisc_util dsmark_qdisc_util = { + .id = "dsmark", + .parse_qopt = dsmark_parse_opt, + .print_qopt = dsmark_print_opt, + .parse_copt = dsmark_parse_class_opt, + .print_copt = dsmark_print_opt, +}; diff --git a/tc/q_dsmark.o b/tc/q_dsmark.o new file mode 100644 index 0000000000000000000000000000000000000000..e2b6575fe11ca2f5ff9c04ae6883b0cfdaca8736 GIT binary patch literal 5312 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 QKIu<-aE0)XJ-CSb5B5g;)Bpeg literal 0 HcmV?d00001 diff --git a/tc/q_fifo.c b/tc/q_fifo.c new file mode 100644 index 0000000..9f3b3eb --- /dev/null +++ b/tc/q_fifo.c @@ -0,0 +1,98 @@ +/* + * q_fifo.c FIFO. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... [p|b]fifo [ limit NUMBER ]\n"); +} + +#define usage() return(-1) + +static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_fifo_qopt opt; + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (ok) + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +static int fifo_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_fifo_qopt *qopt; + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + if (strcmp(qu->id, "bfifo") == 0) { + SPRINT_BUF(b1); + fprintf(f, "limit %s", sprint_size(qopt->limit, b1)); + } else + fprintf(f, "limit %up", qopt->limit); + return 0; +} + + +struct qdisc_util bfifo_qdisc_util = { + .id = "bfifo", + .parse_qopt = fifo_parse_opt, + .print_qopt = fifo_print_opt, +}; + +struct qdisc_util pfifo_qdisc_util = { + .id = "pfifo", + .parse_qopt = fifo_parse_opt, + .print_qopt = fifo_print_opt, +}; + +extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +struct qdisc_util pfifo_fast_qdisc_util = { + .id = "pfifo_fast", + .print_qopt = prio_print_opt, +}; diff --git a/tc/q_fifo.o b/tc/q_fifo.o new file mode 100644 index 0000000000000000000000000000000000000000..bca57fc1178b77871e404af4bac1ea605ac275fd GIT binary patch literal 3608 zcmds3Uuaup6#v>}Tdg(Cber`bTuL`PWS68XwG7mxYr0#^Mp+mCP;0zR?rkroNtSzK zqm)$$k%icj$32P<;=2!a1-rP_`7n?=@Iidg!H`M?83Xm95YPGU`O@3l1Rs3!K<+u` zcm93fx%tj_mm<-z_O>=AsExhMb~S-Ami6w+AufhkH|t_b<uTOOPQYUw18<=E8$NWs zs#n9WjhDX*$HJE)!TT|#{KI7Vp;G+aU;6Xg(Y`M9x-uF5%Jv3#y&A1V*!6<|M9Y8d zM9a4yewKfyen%;O(^2}Vd*ur-D3v@@mKXcFwlJ(z%P-aMgzFo&%hHWV>3+UjDQ@_K zwW;awTjA;O+qabEXkS-&6}Pa~4v%#PVzm4h&#--Whp+ki0kH6j)-k2>5-6bETt^<P zanG&pdcD31zdGjM%}y=wgXeAwcm#Le{+`R&4!t@#UfvAeRd^Rd=Swwfe7yThwq_^Y z@+!r@J?{-Em4%K~Yyw}{j#B;_+Rz`a6ue6LSDuV7ODW$}%0JWMAZc+URNkDr6|PKp z$u5lB_i!8kC(7!Eo{8(KX)!Lc83b6$m^Umor=_y&^*Pm&3{&boZuY*?$;MMDZB|W5 zz1*d@({`1Pn?lHEu|F|Woz>1rvMfte*~OV@%qmSuJV?3_J3kV+C{1@Vc7&SQ#@Y)0 zu0tJFIy8tsOEzTBkUR(+e}X#6f5hisX^;4NMtsu8-eu3Ium7V1qrO1#;FvFjzL+b8 z0#Si@*yk^{M|~6Q+}X2dr2cakVkf23@}N8*4Fm#%!9egussEyuloV(J$4?E(dgAm+ z#$@yTc}txEZslw~C#0N~Qe{gkSWM<(Qngej&zL6Y8B{PPC<phd!{n{a#r2#zuQ55D zu{0T$bjq~U#JfyRWaj6!v?ccNU$_Lp*g8BNP70CoIrUZ8@K8Dl--lzAHB5m=ZSfb1 z$Ek*52YK`me}Qlh=?#W+1!&b~ji=O*YyszFv-*BMMj5M-KdM;6T|Y>eyZ$QGr|<@I z*BSx~?rWgoVGANY=E6lg1&@HeS-hVX^t>xxtY5^xN%8LG4paQq7V!eU1cG}{XxpUz zDddPM))w`}`%3^O)<UkF)!YpwqubZ~mw0?XPIAW|24k~Q4+vaz27~O5N8Q~!3<=Oi zLuB*;CVGH&vHwRvZdN}FGYyH~IJU^2?)nbX9ufV#>_L4GNQ!6QE^LFR=}K^Ycopv_ z&%RU77%saV@=N3{=IT>~2WSHYvLo}bLk`Ur`+;E|L1J#B?rHpsgw^IAUVSfynupOa zahL)i9|nt{VG_oGyJr-d@EoFl2o)gVuY-*EhX?`*d;#QUd9Jj;OD*t~7WlOm_$MuJ z{8DSS9(Fm7SE`&~`~uIaIa7;gvKFa1BW-b2D`Zouk+v^)e$U4*Ck-<Z&s#={WphR* z4i9p+KL#9j{xRSTFs-DP%Q1Z+XIL7W)vUN_e4sHk3AdV+i>H{*v+2x8!)29CX_gkp zTT4wRnQ0f|0gX3^#|+Q%4t}`-Jq|Jw{vXB6K^^BM65{x;ei9WRan3Q%6Yvvx_=g+l zh#zs_w?Re{d7iUX_9JokdhQV}{Ci2?a`+zwS=2p9e|>kJqlAk*V!l1?@W;K2JSQCZ zF$W%Z<-y(|nGS#KjmT4Q`HygK_}+K;JJ)~B<^L-Af7-%dya(K`bN%AoVVH9Y^-w;< zakdIlND%(w<WD$otRa#9h6@+->8b<Ax}rZ-2ky*+=Mf3(A};bsAR`If)^_KFz$G$d Ji3?oh`3GHG*vkL_ literal 0 HcmV?d00001 diff --git a/tc/q_gred.c b/tc/q_gred.c new file mode 100644 index 0000000..0526c75 --- /dev/null +++ b/tc/q_gred.c @@ -0,0 +1,312 @@ +/* + * q_gred.c GRED. + * + * 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: J Hadi Salim(hadi@nortelnetworks.com) + * code ruthlessly ripped from + * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 "tc_red.h" + + +#if 0 +#define DPRINTF(format,args...) fprintf(stderr,format,##args) +#else +#define DPRINTF(format,args...) +#endif + +static void explain(void) +{ + fprintf(stderr, "Usage: ... gred DP drop-probability limit BYTES " + "min BYTES max BYTES\n"); + fprintf(stderr, " avpkt BYTES burst PACKETS probability PROBABILITY " + "bandwidth KBPS\n"); + fprintf(stderr, " [prio value]\n"); + fprintf(stderr," OR ...\n"); + fprintf(stderr," gred setup DPs <num of DPs> default <default DP> " + "[grio]\n"); +} + +#define usage() return(-1) + +static int init_gred(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + + struct rtattr *tail; + struct tc_gred_sopt opt; + memset(&opt, 0, sizeof(struct tc_gred_sopt)); + + while (argc > 0) { + DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); + if (strcmp(*argv, "DPs") == 0) { + NEXT_ARG(); + DPRINTF(stderr,"init_gred: next_arg with %s\n",*argv); + opt.DPs=strtol(*argv, (char **)NULL, 10); + if (opt.DPs >MAX_DPs) { /* need a better error check */ + fprintf(stderr, "DPs =%u \n",opt.DPs); + fprintf(stderr, "Illegal \"DPs\"\n"); + fprintf(stderr, "GRED: only %d DPs are " + "currently supported\n",MAX_DPs); + return -1; + } + } else if (strcmp(*argv, "default") == 0) { + NEXT_ARG(); + opt.def_DP=strtol(*argv, (char **)NULL, 10); + if (!opt.DPs) { + fprintf(stderr, "\"default DP\" must be " + "defined after DPs\n"); + return -1; + } + if (opt.def_DP>opt.DPs) { +/* + fprintf(stderr, "\"default DP\" must be less than %d\nNote: DP runs from 0 to %d for %d DPs\n",opt.DPs,opt.DPs-1,opt.DPs); +*/ + fprintf(stderr, "\"default DP\" must be less than %d\n",opt.DPs); + return -1; + } + } else if (strcmp(*argv, "grio") == 0) { + opt.grio=1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; +} + +if ((!opt.DPs) || (!opt.def_DP)) +{ + fprintf(stderr, "Illegal gred setup parameters \n"); + return -1; +} +DPRINTF("TC_GRED: sending DPs=%d default=%d\n",opt.DPs,opt.def_DP); + n->nlmsg_flags|=NLM_F_CREATE; + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; +return 0; +} +/* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +*/ +static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_gred_qopt opt; + unsigned burst = 0; + unsigned avpkt = 0; + double probability = 0.02; + unsigned rate = 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_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "setup") == 0) { + if (ok) { + fprintf(stderr, "Illegal \"setup\"\n"); + return -1; + } + return init_gred(qu,argc-1, argv+1,n); + + } else if (strcmp(*argv, "min") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_min, *argv)) { + fprintf(stderr, "Illegal \"min\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_max, *argv)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "DP") == 0) { + NEXT_ARG(); + opt.DP=strtol(*argv, (char **)NULL, 10); + DPRINTF ("\n ******* DP =%u\n",opt.DP); + if (opt.DP >MAX_DPs) { /* need a better error check */ + fprintf(stderr, "DP =%u \n",opt.DP); + fprintf(stderr, "Illegal \"DP\"\n"); + fprintf(stderr, "GRED: only %d DPs are currently supported\n",MAX_DPs); + return -1; + } + ok++; + } else if (strcmp(*argv, "burst") == 0) { + NEXT_ARG(); + if (get_unsigned(&burst, *argv, 0)) { + fprintf(stderr, "Illegal \"burst\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + fprintf(stderr, "Illegal \"avpkt\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "probability") == 0) { + NEXT_ARG(); + if (sscanf(*argv, "%lg", &probability) != 1) { + fprintf(stderr, "Illegal \"probability\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "prio") == 0) { + NEXT_ARG(); + opt.prio=strtol(*argv, (char **)NULL, 10); + /* some error check here */ + ok++; + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (rate == 0) + get_rate(&rate, "10Mbit"); + + if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt || + (opt.DP<0)) { + fprintf(stderr, "Required parameter (min, max, burst, limit, " + "avpket, DP) is missing\n"); + return -1; + } + + if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); + return -1; + } + if (wlog >= 10) + fprintf(stderr, "GRED: WARNING. Burst %d seems to be to " + "large.\n", burst); + opt.Wlog = wlog; + if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + fprintf(stderr, "GRED: failed to calculate probability.\n"); + return -1; + } + opt.Plog = wlog; + if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) + { + fprintf(stderr, "GRED: failed to calculate idle damping " + "table.\n"); + return -1; + } + opt.Scell_log = wlog; + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_GRED_STAB+1]; + struct tc_gred_qopt *qopt; + int i; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + SPRINT_BUF(b4); + SPRINT_BUF(b5); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_GRED_STAB, opt); + + if (tb[TCA_GRED_PARMS] == NULL) + return -1; + + qopt = RTA_DATA(tb[TCA_GRED_PARMS]); + if (RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { + fprintf(f,"\n GRED received message smaller than expected\n"); + return -1; + } + +/* Bad hack! should really return a proper message as shown above*/ + + for (i=0;i<MAX_DPs;i++, qopt++) { + if (qopt->DP >= MAX_DPs) continue; + fprintf(f, "\n DP:%d (prio %d) Average Queue %s Measured " + "Queue %s ", + qopt->DP, + qopt->prio, + sprint_size(qopt->qave, b4), + sprint_size(qopt->backlog, b5)); + fprintf(f, "\n\t Packet drops: %d (forced %d early %d) ", + qopt->forced+qopt->early, + qopt->forced, + qopt->early); + fprintf(f, "\n\t Packet totals: %u (bytes %u) ", + qopt->packets, + qopt->bytesin); + if (show_details) + fprintf(f, "\n limit %s min %s max %s ", + sprint_size(qopt->limit, b1), + sprint_size(qopt->qth_min, b2), + sprint_size(qopt->qth_max, b3)); + fprintf(f, "ewma %u Plog %u Scell_log %u", + qopt->Wlog, qopt->Plog, qopt->Scell_log); + } + return 0; +} + +struct qdisc_util gred_qdisc_util = { + .id = "gred", + .parse_qopt = gred_parse_opt, + .print_qopt = gred_print_opt, +}; diff --git a/tc/q_gred.o b/tc/q_gred.o new file mode 100644 index 0000000000000000000000000000000000000000..cb44aad9060ceb88f5744b55caf6e19b9ee1af49 GIT binary patch literal 9664 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 literal 0 HcmV?d00001 diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c new file mode 100644 index 0000000..f09c606 --- /dev/null +++ b/tc/q_hfsc.c @@ -0,0 +1,406 @@ +/* + * q_hfsc.c HFSC. + * + * 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: Patrick McHardy, <kaber@trash.net> + * + */ + +#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 <math.h> + +#include "utils.h" +#include "tc_util.h" + +static int hfsc_get_sc(int *, char ***, struct tc_service_curve *); + + +static void +explain_qdisc(void) +{ + fprintf(stderr, + "Usage: ... hfsc [ default CLASSID ]\n" + "\n" + " default: default class for unclassified packets\n" + ); +} + +static void +explain_class(void) +{ + fprintf(stderr, + "Usage: ... hfsc [ [ rt SC ] [ ls SC ] | [ sc SC ] ] [ ul SC ]\n" + "\n" + "SC := [ [ m1 BPS ] [ d SEC ] m2 BPS\n" + "\n" + " m1 : slope of first segment\n" + " d : x-coordinate of intersection\n" + " m2 : slope of second segment\n" + "\n" + "Alternative format:\n" + "\n" + "SC := [ [ umax BYTE ] dmax SEC ] rate BPS\n" + "\n" + " umax : maximum unit of work\n" + " dmax : maximum delay\n" + " rate : rate\n" + "\n" + ); +} + +static void +explain1(char *arg) +{ + fprintf(stderr, "HFSC: Illegal \"%s\"\n", arg); +} + +static int +hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_hfsc_qopt qopt; + + memset(&qopt, 0, sizeof(qopt)); + + while (argc > 0) { + if (matches(*argv, "default") == 0) { + NEXT_ARG(); + if (qopt.defcls != 0) { + fprintf(stderr, "HFSC: Double \"default\"\n"); + return -1; + } + if (get_u16(&qopt.defcls, *argv, 16) < 0) { + explain1("default"); + return -1; + } + } else if (matches(*argv, "help") == 0) { + explain_qdisc(); + return -1; + } else { + fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); + explain_qdisc(); + return -1; + } + argc--, argv++; + } + + addattr_l(n, 1024, TCA_OPTIONS, &qopt, sizeof(qopt)); + return 0; +} + +static int +hfsc_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_hfsc_qopt *qopt; + + if (opt == NULL) + return 0; + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + + if (qopt->defcls != 0) + fprintf(f, "default %x ", qopt->defcls); + + return 0; +} + +static int +hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_hfsc_stats *st; + + if (xstats == NULL) + return 0; + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + st = RTA_DATA(xstats); + + fprintf(f, " period %u ", st->period); + if (st->work != 0) + fprintf(f, "work %llu bytes ", (unsigned long long) st->work); + if (st->rtwork != 0) + fprintf(f, "rtwork %llu bytes ", (unsigned long long) st->rtwork); + fprintf(f, "level %u ", st->level); + fprintf(f, "\n"); + + return 0; +} + +static int +hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + struct tc_service_curve rsc, fsc, usc; + int rsc_ok, fsc_ok, usc_ok; + struct rtattr *tail; + + memset(&rsc, 0, sizeof(rsc)); + memset(&fsc, 0, sizeof(fsc)); + memset(&usc, 0, sizeof(usc)); + rsc_ok = fsc_ok = usc_ok = 0; + + while (argc > 0) { + if (matches(*argv, "rt") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + explain1("rt"); + return -1; + } + rsc_ok = 1; + } else if (matches(*argv, "ls") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &fsc) < 0) { + explain1("ls"); + return -1; + } + fsc_ok = 1; + } else if (matches(*argv, "sc") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + explain1("sc"); + return -1; + } + memcpy(&fsc, &rsc, sizeof(fsc)); + rsc_ok = 1; + fsc_ok = 1; + } else if (matches(*argv, "ul") == 0) { + NEXT_ARG(); + if (hfsc_get_sc(&argc, &argv, &usc) < 0) { + explain1("ul"); + return -1; + } + usc_ok = 1; + } else if (matches(*argv, "help") == 0) { + explain_class(); + return -1; + } else { + fprintf(stderr, "HFSC: What is \"%s\" ?\n", *argv); + explain_class(); + return -1; + } + argc--, argv++; + } + + if (!(rsc_ok || fsc_ok || usc_ok)) { + fprintf(stderr, "HFSC: no parameters given\n"); + explain_class(); + return -1; + } + if (usc_ok && !fsc_ok) { + fprintf(stderr, "HFSC: Upper-limit Service Curve without " + "Link-Share Service Curve\n"); + explain_class(); + return -1; + } + + tail = NLMSG_TAIL(n); + + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + if (rsc_ok) + addattr_l(n, 1024, TCA_HFSC_RSC, &rsc, sizeof(rsc)); + if (fsc_ok) + addattr_l(n, 1024, TCA_HFSC_FSC, &fsc, sizeof(fsc)); + if (usc_ok) + addattr_l(n, 1024, TCA_HFSC_USC, &usc, sizeof(usc)); + + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static void +hfsc_print_sc(FILE *f, char *name, struct tc_service_curve *sc) +{ + SPRINT_BUF(b1); + + fprintf(f, "%s ", name); + fprintf(f, "m1 %s ", sprint_rate(sc->m1, b1)); + fprintf(f, "d %s ", sprint_usecs(sc->d, b1)); + fprintf(f, "m2 %s ", sprint_rate(sc->m2, b1)); +} + +static int +hfsc_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_HFSC_MAX+1]; + struct tc_service_curve *rsc = NULL, *fsc = NULL, *usc = NULL; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_HFSC_MAX, opt); + + if (tb[TCA_HFSC_RSC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_RSC]) < sizeof(*rsc)) + fprintf(stderr, "HFSC: truncated realtime option\n"); + else + rsc = RTA_DATA(tb[TCA_HFSC_RSC]); + } + if (tb[TCA_HFSC_FSC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_FSC]) < sizeof(*fsc)) + fprintf(stderr, "HFSC: truncated linkshare option\n"); + else + fsc = RTA_DATA(tb[TCA_HFSC_FSC]); + } + if (tb[TCA_HFSC_USC]) { + if (RTA_PAYLOAD(tb[TCA_HFSC_USC]) < sizeof(*usc)) + fprintf(stderr, "HFSC: truncated upperlimit option\n"); + else + usc = RTA_DATA(tb[TCA_HFSC_USC]); + } + + + if (rsc != NULL && fsc != NULL && + memcmp(rsc, fsc, sizeof(*rsc)) == 0) + hfsc_print_sc(f, "sc", rsc); + else { + if (rsc != NULL) + hfsc_print_sc(f, "rt", rsc); + if (fsc != NULL) + hfsc_print_sc(f, "ls", fsc); + } + if (usc != NULL) + hfsc_print_sc(f, "ul", usc); + + return 0; +} + +struct qdisc_util hfsc_qdisc_util = { + .id = "hfsc", + .parse_qopt = hfsc_parse_opt, + .print_qopt = hfsc_print_opt, + .print_xstats = hfsc_print_xstats, + .parse_copt = hfsc_parse_class_opt, + .print_copt = hfsc_print_class_opt, +}; + +static int +hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + char **argv = *argvp; + int argc = *argcp; + unsigned int m1 = 0, d = 0, m2 = 0; + + if (matches(*argv, "m1") == 0) { + NEXT_ARG(); + if (get_rate(&m1, *argv) < 0) { + explain1("m1"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "d") == 0) { + NEXT_ARG(); + if (get_usecs(&d, *argv) < 0) { + explain1("d"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "m2") == 0) { + NEXT_ARG(); + if (get_rate(&m2, *argv) < 0) { + explain1("m2"); + return -1; + } + } else + return -1; + + sc->m1 = m1; + sc->d = d; + sc->m2 = m2; + + *argvp = argv; + *argcp = argc; + return 0; +} + +static int +hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + char **argv = *argvp; + int argc = *argcp; + unsigned int umax = 0, dmax = 0, rate = 0; + + if (matches(*argv, "umax") == 0) { + NEXT_ARG(); + if (get_size(&umax, *argv) < 0) { + explain1("umax"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "dmax") == 0) { + NEXT_ARG(); + if (get_usecs(&dmax, *argv) < 0) { + explain1("dmax"); + return -1; + } + NEXT_ARG(); + } + + if (matches(*argv, "rate") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv) < 0) { + explain1("rate"); + return -1; + } + } else + return -1; + + if (umax != 0 && dmax == 0) { + fprintf(stderr, "HFSC: umax given but dmax is zero.\n"); + return -1; + } + + if (dmax != 0 && ceil(umax * 1000000.0 / dmax) > rate) { + /* + * concave curve, slope of first segment is umax/dmax, + * intersection is at dmax + */ + sc->m1 = ceil(umax * 1000000.0 / dmax); /* in bps */ + sc->d = dmax; + sc->m2 = rate; + } else { + /* + * convex curve, slope of first segment is 0, intersection + * is at dmax - umax / rate + */ + sc->m1 = 0; + sc->d = ceil(dmax - umax * 1000000.0 / rate); /* in usec */ + sc->m2 = rate; + } + + *argvp = argv; + *argcp = argc; + return 0; +} + +static int +hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc) +{ + if (hfsc_get_sc1(argcp, argvp, sc) < 0 && + hfsc_get_sc2(argcp, argvp, sc) < 0) + return -1; + + if (sc->m1 == 0 && sc->m2 == 0) { + fprintf(stderr, "HFSC: Service Curve has two zero slopes\n"); + return -1; + } + + return 0; +} diff --git a/tc/q_hfsc.o b/tc/q_hfsc.o new file mode 100644 index 0000000000000000000000000000000000000000..7f8a979994c55b27506986f6a318c8475111c8c5 GIT binary patch literal 10408 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 F@PDViHCX@v literal 0 HcmV?d00001 diff --git a/tc/q_htb.c b/tc/q_htb.c new file mode 100644 index 0000000..828d4b1 --- /dev/null +++ b/tc/q_htb.c @@ -0,0 +1,330 @@ +/* + * q_htb.c HTB. + * + * 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: Martin Devera, devik@cdi.cz + * + */ + +#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" + +#define HTB_TC_VER 0x30003 +#if HTB_TC_VER >> 16 != TC_HTB_PROTOVER +#error "Different kernel and TC HTB versions" +#endif + +static void explain(void) +{ + fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\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" + "... 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" + " rate rate allocated to this class (class can still borrow)\n" + " burst max bytes burst which can be accumulated during idle period {computed}\n" + " mpu minimum packet size used in rate computations\n" + " overhead per-packet size overhead used in rate computations\n" + + " ceil definite upper class rate (no borrows) {rate}\n" + " cburst burst but for ceil {computed}\n" + " mtu max packet size we create rate map for {1600}\n" + " prio priority of leaf; lower are served first {0}\n" + " quantum how much bytes to serve from leaf at once {use r2q}\n" + "\nTC HTB version %d.%d\n",HTB_TC_VER>>16,HTB_TC_VER&0xffff + ); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); + explain(); +} + + +#define usage() return(-1) + +static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + struct tc_htb_glob opt; + struct rtattr *tail; + unsigned i; char *p; + memset(&opt,0,sizeof(opt)); + opt.rate2quantum = 10; + opt.version = 3; + + while (argc > 0) { + if (matches(*argv, "r2q") == 0) { + NEXT_ARG(); + if (get_u32(&opt.rate2quantum, *argv, 10)) { + explain1("r2q"); return -1; + } + } else if (matches(*argv, "default") == 0) { + NEXT_ARG(); + if (get_u32(&opt.defcls, *argv, 16)) { + explain1("default"); return -1; + } + } else if (matches(*argv, "debug") == 0) { + NEXT_ARG(); p = *argv; + for (i=0; i<16; i++,p++) { + if (*p<'0' || *p>'3') break; + opt.debug |= (*p-'0')<<(2*i); + } + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt))); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_htb_opt opt; + __u32 rtab[256],ctab[256]; + unsigned buffer=0,cbuffer=0; + int cell_log=-1,ccell_log = -1; + unsigned mtu, mpu; + unsigned char mpu8 = 0, overhead = 0; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); mtu = 1600; /* eth packet len */ + + while (argc > 0) { + if (matches(*argv, "prio") == 0) { + NEXT_ARG(); + if (get_u32(&opt.prio, *argv, 10)) { + explain1("prio"); return -1; + } + ok++; + } else if (matches(*argv, "mtu") == 0) { + NEXT_ARG(); + if (get_u32(&mtu, *argv, 10)) { + explain1("mtu"); return -1; + } + } else if (matches(*argv, "mpu") == 0) { + NEXT_ARG(); + if (get_u8(&mpu8, *argv, 10)) { + explain1("mpu"); return -1; + } + } else if (matches(*argv, "overhead") == 0) { + NEXT_ARG(); + if (get_u8(&overhead, *argv, 10)) { + explain1("overhead"); return -1; + } + } else if (matches(*argv, "quantum") == 0) { + NEXT_ARG(); + if (get_u32(&opt.quantum, *argv, 10)) { + explain1("quantum"); return -1; + } + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (get_size_and_cell(&buffer, &cell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + ok++; + } else if (matches(*argv, "cburst") == 0 || + strcmp(*argv, "cbuffer") == 0 || + strcmp(*argv, "cmaxburst") == 0) { + NEXT_ARG(); + if (get_size_and_cell(&cbuffer, &ccell_log, *argv) < 0) { + explain1("cbuffer"); + return -1; + } + ok++; + } else if (strcmp(*argv, "ceil") == 0) { + NEXT_ARG(); + if (opt.ceil.rate) { + fprintf(stderr, "Double \"ceil\" spec\n"); + return -1; + } + if (get_rate(&opt.ceil.rate, *argv)) { + explain1("ceil"); + return -1; + } + ok++; + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (opt.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&opt.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + +/* if (!ok) + return 0;*/ + + if (opt.rate.rate == 0) { + fprintf(stderr, "\"rate\" is required.\n"); + return -1; + } + /* if ceil params are missing, use the same as rate */ + if (!opt.ceil.rate) opt.ceil = opt.rate; + + /* compute minimal allowed burst from rate; mtu is added here to make + sute that buffer is larger than mtu and to have some safeguard space */ + if (!buffer) buffer = opt.rate.rate / get_hz() + mtu; + if (!cbuffer) cbuffer = opt.ceil.rate / get_hz() + mtu; + +/* encode overhead and mpu, 8 bits each, into lower 16 bits */ + mpu = (unsigned)mpu8 | (unsigned)overhead << 8; + opt.ceil.mpu = mpu; opt.rate.mpu = mpu; + + if ((cell_log = tc_calc_rtable(opt.rate.rate, rtab, cell_log, mtu, mpu)) < 0) { + fprintf(stderr, "htb: failed to calculate rate table.\n"); + return -1; + } + opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer); + opt.rate.cell_log = cell_log; + + if ((ccell_log = tc_calc_rtable(opt.ceil.rate, ctab, cell_log, mtu, mpu)) < 0) { + fprintf(stderr, "htb: failed to calculate ceil rate table.\n"); + return -1; + } + opt.cbuffer = tc_calc_xmittime(opt.ceil.rate, cbuffer); + opt.ceil.cell_log = ccell_log; + + tail = NLMSG_TAIL(n); + 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); + addattr_l(n, 4024, TCA_HTB_CTAB, ctab, 1024); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_HTB_RTAB+1]; + struct tc_htb_opt *hopt; + struct tc_htb_glob *gopt; + double buffer,cbuffer; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_HTB_RTAB, opt); + + if (tb[TCA_HTB_PARMS]) { + + hopt = RTA_DATA(tb[TCA_HTB_PARMS]); + if (RTA_PAYLOAD(tb[TCA_HTB_PARMS]) < sizeof(*hopt)) return -1; + + if (!hopt->level) { + fprintf(f, "prio %d ", (int)hopt->prio); + if (show_details) + fprintf(f, "quantum %d ", (int)hopt->quantum); + } + fprintf(f, "rate %s ", sprint_rate(hopt->rate.rate, b1)); + buffer = ((double)hopt->rate.rate*tc_core_tick2usec(hopt->buffer))/1000000; + fprintf(f, "ceil %s ", sprint_rate(hopt->ceil.rate, b1)); + cbuffer = ((double)hopt->ceil.rate*tc_core_tick2usec(hopt->cbuffer))/1000000; + if (show_details) { + fprintf(f, "burst %s/%u mpu %s overhead %s ", + sprint_size(buffer, b1), + 1<<hopt->rate.cell_log, + sprint_size(hopt->rate.mpu&0xFF, b2), + sprint_size((hopt->rate.mpu>>8)&0xFF, b3)); + fprintf(f, "cburst %s/%u mpu %s overhead %s ", + sprint_size(cbuffer, b1), + 1<<hopt->ceil.cell_log, + sprint_size(hopt->ceil.mpu&0xFF, b2), + sprint_size((hopt->ceil.mpu>>8)&0xFF, b3)); + fprintf(f, "level %d ", (int)hopt->level); + } else { + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + fprintf(f, "cburst %s ", sprint_size(cbuffer, b1)); + } + if (show_raw) + fprintf(f, "buffer [%08x] cbuffer [%08x] ", + hopt->buffer,hopt->cbuffer); + } + if (tb[TCA_HTB_INIT]) { + gopt = RTA_DATA(tb[TCA_HTB_INIT]); + if (RTA_PAYLOAD(tb[TCA_HTB_INIT]) < sizeof(*gopt)) return -1; + + fprintf(f, "r2q %d default %x direct_packets_stat %u", + gopt->rate2quantum,gopt->defcls,gopt->direct_pkts); + if (show_details) + fprintf(f," ver %d.%d",gopt->version >> 16,gopt->version & 0xffff); + } + return 0; +} + +static int htb_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ + struct tc_htb_xstats *st; + if (xstats == NULL) + return 0; + + if (RTA_PAYLOAD(xstats) < sizeof(*st)) + return -1; + + st = RTA_DATA(xstats); + fprintf(f, " lended: %u borrowed: %u giants: %u\n", + st->lends,st->borrows,st->giants); + fprintf(f, " tokens: %d ctokens: %d\n", st->tokens,st->ctokens); + return 0; +} + +struct qdisc_util htb_qdisc_util = { + .id = "htb", + .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, +}; + +/* 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, +}; diff --git a/tc/q_htb.o b/tc/q_htb.o new file mode 100644 index 0000000000000000000000000000000000000000..e00de5bc08ef4be1659a457f4c45dd9a4b233362 GIT binary patch literal 11496 zcmd5?e{fXCeP8L$Agi)YwiSmO#oj7Uj<E4rCp1{WKO!N?JlO{!AW>6+kJH^dbm4R- z?!5zHENpTT;q_TEnQ5CelO`D_@k}TD;YnRA6Gjm~YM_%QwrM-Ynbc~SG*SV_He*9D z>Gk{FAE(umUiwG=>CW8k?q|RI`TpA7x9_B9JHn5ZmXr`#O30&RwkJ_S&Q#B~TUfJ& zRFPYV?mU9M*L?7G6u&008=9QKDv>y(=P#A%_H`j92B&dQ&z~y0IN91An!M54)oK6q zwNe1P?T<F<_BohUKXO^Of75CI`ilO;)9sqS97a$3HB@nWF?^AIk0+4dd5DoR%8|*y z$RQXi{EMDHSvGPxFm@mKqB|=(M~D1Jk)bHI2nM9svqw8ZlcBRm(SQl!CnRkDrq!v@ zm~iVjdLFiC`0zC7hT;54A0Ku6TK>X~aLI-Iq%S`Ojj6=4w-dH6GztFI_iSz5(z^Ae z%g9`_)EpT5OCaY4mL0893>=OW3Wc8lK&D^&pF&|AdWDOJ-9%lIXkQ1AgbIm6fg^W} zu72ZTU*M~E!Hm(0F9aq|6i(--8}!2K=caCM*DCUbz^?Or-V7-!=>jDe|CafrJGU>8 zuIbyP88fvo3uBL;)=ikdZl1xLz+9QZ{g>945i)W)JbGB8(1IMIe^^70)z!dLfPMwm zVf(kQw`)rYeBDVf=Pb!ETGO%!lD(EIoS$z3S(8x+jFrIp;Nh&Fo9`wVkYdjsb&hHE zXk8&N{2~lE&udNCJ)8Tq?hG???PCnm?O|;>3^`p|)yP}9yPQv<?IUjmhWCM9`%U{= z=&h?C_`pgV{ByuJT!S}Yam`9T1d_mt8tgYw+zD$nC1+qlS>OnG8#uB>TN)mHMWe9v zKv8wr{`mM*+07?gZCFX)(i&kR*7ra?&D2lA-0Hx=3N+mUwcHzhG^J4{fF!5{2w)5q z>-Kx#Ni(z#{uT3YmZ&0da0)H7v@#*g#z6dG5C@kk7Tz5Pd1RPy>-cgo3^xg$9TV<N zGR`;E2S>p{GV#Wr@GW%VhM0?^I?M%k$i#Snjlz!NqYiAad0T7$DO_B9ceOq>Z-1?l znGTG-51g<d^wDRunmH?C_`ToD4ff<Ga)Z6(USGLKW~qKKOSN<^)%S{2w*b|}f56z! zP}nc-{rY9ewy#GRb?02B%|)h@*9(Q21Z)fz29aX>gpGTQ%p3-}1qYIHz^%HYsCtTT z5IR@$yEkMwI#Kf_upYM@KY~^wbbh`=RzukFjpI&bayNAQc-VgZ%1hRg@tY+G0KivX z3XJ>>K+=l9*e37|kD5_StH$(HF@IqD*j+_aCVmHcqfGBDFqs_1vv>m67`F>Cb~Mh+ zfZw{qh3e)DT|6%b?_FT<7<+_-?bE^pYdN}A29hqfs>ksm!a(U(?!j4?^2}1^QU$tH zR&;4UtP5`P#cSZw7+5=wnUqu*xhpT_9-ifT5V#=g>|843ES|G!NjTh4B>W~4KH@?6 z%q-!5fr#@8KleV)wQ<6tg~RVcin)cINO%cyAVYTh{E*EYP?ECHcml>3@X)$LJX*r` z3H35C{mNCb2WRdE&El|!^%u6E(XQ$C7VVADS=~-)Z-?!;Hq~ig4MS%-Y+Ks<`u<DJ zuq1Rmm5)Iq@M^g}dQ5u*tk&}<{1bTS<Y)XWba=eKjRUaGhFfsfXrEsXyh>ifXONzs z_Urj;_XQ4EFywS-C^ZI$Bc9HOV45>j28T0aV%F>SiLhM=O)|vkD07CEe5eQRo%}Fx zE1d2OWqp|B3{`&^*bL~0fv4CWR%909s&<BIB$uCGMOfEilSe?sX1}Iz`L)1E0ZdE; zZVPK@B}gy-orkF)<ky7EH4m9~zMDEm&RTamYipeDC1RIIfAIzY=jFGwDHhOQSS=7p zpFV2()nMMf>%Mm9u_|X>S!mMP<cDxE8m6?JJJW&B%yNi0q!{SRFfGnutqN-UOxXFp zwgg&XKq#wW4YT@~)&RA>{}g8bq*yY6BSVYvNU8cUfc$X@&?1D^ciR8N)Pnb0Scp39 zQy8UB7#cXTme}vCwP)7aQ_{+Fzp~$i9E$`45!>7Rq=MdUZrW-Ls$jh?Su6M|;mpkW z(B!!b_IWn$bZf4#&fqEHZ1m5<fmi?e_<wVy>=1Uk>qC=rn6rpQ5g6WpyS$sqQDOPa zLjbdUb>lXIJ2qa+N8Ym5K*~zzrmIOG{R_Xdf$P`p-$L4+@V7f{d^tN$R5=?f7(E+f z>VY$Vs?0vqv-Knd{r^Sjvw9l|e38yn%Ge%F(b_t*wwz=ecape~h~`oj)V;aw<j?z} z7EPLL{0rq|Ae+pPek(`%2XZ8{%gFW_(Ky+ei>9qyKk3b7O$)vg2_sATql3I(n~hpV zEs|u7ow;PzhzH9_jE_OZgkweNn2}5ooI%<%x!#mPYZ0lX=713^FJhdMV|_+yfX$$F zCM77%8cYF)W}93c?Wt>M9^6WWcG)E<W0wJw<CIWv4I06^ICGg{!b?7GWp)^8a}{=C zvu%9*vC?fuw;QWyFc_pe<4H3{qwzTFLCk572r*r^mG(f$p)RKqqy5QrCQFlXnn}=D zDr%b4%Fx|?$yguFr5TV+B#k&7h{kppmPw;ogPKO#qI(;L$|=&Z(!OB>6&7=uSO#)t z!Ne#e0^&=PX}WsAd;n+c4dGmtahz;g*<^Y<8W37Z)4Bd$BWqG43Klfnzk=y0N6)x* zs%MOMLx}e9h_4QTgP@hJhFY!``bF?|!P0}Pi>?P*GnKLEMraJM*7}W}Q&_q;G=eB! zQFJx7VTIDQUER=<N&6BoFzZk=YeZA2Obp@>$M{-(uspdx^}LEj(-4PbDn)xU*=%O_ zvU19ug88smY40A(Fa^Z3gEM*!8jZzr{karlj^|h)lW|zb0|s~(r+Z}{AmA7pRv_oe z{#?JvDK(Q%8#HHvX^<mkET0s$l9{wwPK%)g&inZ~;B$%te_%3;7;1$T2Wnv<<OYC8 zc+a%er!&H9a~a)>5Ev`sONGz9Ig2J>p)$HTPC@LsQxGN9xZMVgWer?;Oi6!qfD!Bs zt!%*6$C8f|v5`$$dvN`wjA-H^n#$}31NlPF?t-u+l5o)Q1uqj!`!c&}e-73t&kQ8h zG(eapvYCFyL*XRLq+<r%3(>)iGE`pP)kgKM)wtVD2rb|7xQSt#uA+%(GR2oiESie3 zlycLpC>%_1i2VUM78d5`tT4#qW~+-AH+$V6jl+Q$vm#<QM@$Q>tjn=O1is|S^*~*Y ztrj@piaBPd-Tx&Zh#&yzxDki*Dkm~4I@^<QbmKXeE1q4?4F_B0∼WU#7YaoHif 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 F{}*j{rMdtB literal 0 HcmV?d00001 diff --git a/tc/q_ingress.c b/tc/q_ingress.c new file mode 100644 index 0000000..71fbd49 --- /dev/null +++ b/tc/q_ingress.c @@ -0,0 +1,69 @@ +/* + * + * q_ingress.c INGRESS. + * + * 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: J Hadi Salim + * + * This is here just in case it is needed + * useless right now; might be useful in the future + * + */ + +#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" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... ingress \n"); +} + +#define usage() return(-1) + +static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + + if (argc > 0) { + while (argc > 0) { + + if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + argc--; argv++; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + } + } + + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + return 0; +} + +static int ingress_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + + fprintf(f, "---------------- "); + return 0; +} + +struct qdisc_util ingress_qdisc_util = { + .id = "ingress", + .parse_qopt = ingress_parse_opt, + .print_qopt = ingress_print_opt, +}; diff --git a/tc/q_ingress.o b/tc/q_ingress.o new file mode 100644 index 0000000000000000000000000000000000000000..5fb4d377ca2907606260a008c8abfed2c8438c26 GIT binary patch literal 2472 zcmb_dO-vI}5S~($3RofWK%&u26Ai?$=~9A*_)`MNVk!reNMcO0v|HNPKX#XZ7f5V~ zX}FMhkRvD0#*2yxR`H<mq={a=dhj4IF&;>8raP})ALU}4bUQQO{JfcYZ{IGD#;!K` zd?3gNm!Yl+6kxrnE=Q|k6oSwOE5B|Gm4A$vKkDTVOWM#&GF@Noe9?kDR{pvhD}VX< zcs??;_inuOIxxCYS&G-?<<ENg>!?=I-fvR+EocV#Mj38vm2G|X5{a?$r!C@IrE1Yv zkCL`|9RIec&{w3oLC^P)wySa~wdVfx()WN~-r4Nj@2^$#(%Zl`#s7cG<Yz6@wjpig zlUWmPr42{P*h=V(9lFvAQ?@Z{4l1guD(tAVLa#&$RYiRg`M|f>(cThR<IOpV7(Id> zu`nF$DA6p*c2O9Shgcp7cDT~m@bIA0JvK4bqeRqxwNL2_hx@g#c3$b8G?R*s^;YrT z0X3C~^Z=;#y_{prAa^X6r-fvhSwnTqMF-TXm^2&%)ER7FwF;zAjHYUAHq-HxW#mjy z^99FLv4&pTF%ow{O%!rDGw*on{#Q}KKpNFyDM6I=HxkZMsi7xO!qd2qx&tm0;C*OE zl|+W2izCi4?q^*vh2t&=fVm>+XHjpoe)x#%sIxz<Pix5QYmCY3PxJa5USslF185=F zzM6+Ci1<+n7ik5*hIW$aEP82!9j3h?%A$S&Q+~N7vMqA|VRoec>8=s+V*WP~6Sc5w zmHlo6Ht<b0z$k%;r}=tGPD}TVJbUu*;eq75!6?h|q|39TkS;#Z>l{G0VH-Yj|1_pX z>p!7kAuYJ9_AjqrGe`pYHN(!H1Xor6zAjuZf5S?szC(@DATGbP0MuubC%?GwPBXs2 znL^Bo`Ku?x1F+}^K17nq53Le{pB+RxJ@t$Cb_8+x&Y<9l1MsN>@H+?Kbp9IUhq*X^ zG1LUG6fYW<ZN>{lr;b>eyz>__m(19S_`H+Jf|0~K*|FkTu$`o7S&%A{dkRtuR>m<g z3EsoStm&9>x{&b;R<DI7M7t)=+a$_FbXMtI+@)_Y67@wyIPIOlO#+cbo@1^GS?1&) z#7Fqs9^9MfKI0<KDb^o&{3*A{^U#CSTm}A=agis&`ZJHeH;?F>-UILH6?;WE^&m*x TM*`nqgTh?ktU^@6H(37zw1W%? literal 0 HcmV?d00001 diff --git a/tc/q_netem.c b/tc/q_netem.c new file mode 100644 index 0000000..f696cc3 --- /dev/null +++ b/tc/q_netem.c @@ -0,0 +1,293 @@ +/* + * q_netem.c NETEM. + * + * 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 <shemminger@osdl.org> + * + */ + +#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 <errno.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void explain(void) +{ + fprintf(stderr, +"Usage: ... netem [ limit PACKETS ] \n" \ +" [ delay TIME [ JITTER [CORRELATION]]]\n" \ +" [ drop PERCENT [CORRELATION]] \n" \ +" [ duplicate PERCENT [CORRELATION]]\n" \ +" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \ +" [ gap PACKETS ]\n"); +} + +static void explain1(const char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + +#define usage() return(-1) + +/* + * Simplistic file parser for distrbution data. + * Format is: + * # comment line(s) + * data0 data1 + */ +#define MAXDIST 65536 +static int get_distribution(const char *type, __s16 *data) +{ + FILE *f; + int n; + long x; + size_t len; + char *line = NULL; + char name[128]; + + snprintf(name, sizeof(name), "/usr/lib/tc/%s.dist", type); + if ((f = fopen(name, "r")) == NULL) { + fprintf(stderr, "No distribution data for %s (%s: %s)\n", + type, name, strerror(errno)); + return -1; + } + + n = 0; + while (getline(&line, &len, f) != -1) { + char *p, *endp; + if (*line == '\n' || *line == '#') + continue; + + for (p = line; ; p = endp) { + x = strtol(p, &endp, 0); + if (endp == p) + break; + + if (n >= MAXDIST) { + fprintf(stderr, "%s: too much data\n", + name); + n = -1; + goto error; + } + data[n++] = x; + } + } + error: + free(line); + fclose(f); + return n; +} + +static int isnumber(const char *arg) +{ + char *p; + (void) strtod(arg, &p); + return (p != arg); +} + +#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1])) + +/* Adjust for the fact that psched_ticks aren't always usecs + (based on kernel PSCHED_CLOCK configuration */ +static int get_ticks(__u32 *ticks, const char *str) +{ + unsigned t; + + if(get_usecs(&t, str)) + return -1; + + *ticks = tc_core_usec2tick(t); + return 0; +} + +static char *sprint_ticks(__u32 ticks, char *buf) +{ + return sprint_usecs(tc_core_tick2usec(ticks), buf); +} + + +static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n) +{ + size_t dist_size = 0; + struct rtattr *tail; + struct tc_netem_qopt opt; + struct tc_netem_corr cor; + __s16 dist_data[MAXDIST]; + + memset(&opt, 0, sizeof(opt)); + opt.limit = 1000; + memset(&cor, 0, sizeof(cor)); + + while (argc > 0) { + if (matches(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_size(&opt.limit, *argv)) { + explain1("limit"); + return -1; + } + } else if (matches(*argv, "latency") == 0 || + matches(*argv, "delay") == 0) { + NEXT_ARG(); + if (get_ticks(&opt.latency, *argv)) { + explain1("latency"); + return -1; + } + + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_ticks(&opt.jitter, *argv)) { + explain1("latency"); + return -1; + } + + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.delay_corr, + *argv)) { + explain1("latency"); + return -1; + } + } + } + } else if (matches(*argv, "loss") == 0 || + matches(*argv, "drop") == 0) { + NEXT_ARG(); + if (get_percent(&opt.loss, *argv)) { + explain1("loss"); + return -1; + } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.loss_corr, *argv)) { + explain1("loss"); + return -1; + } + } + } else if (matches(*argv, "gap") == 0) { + NEXT_ARG(); + if (get_u32(&opt.gap, *argv, 0)) { + explain1("gap"); + return -1; + } + } else if (matches(*argv, "duplicate") == 0) { + NEXT_ARG(); + if (get_percent(&opt.duplicate, *argv)) { + explain1("duplicate"); + return -1; + } + if (NEXT_IS_NUMBER()) { + NEXT_ARG(); + if (get_percent(&cor.dup_corr, *argv)) { + explain1("duplicate"); + return -1; + } + } + } else if (matches(*argv, "distribution") == 0) { + NEXT_ARG(); + dist_size = get_distribution(*argv, dist_data); + if (dist_size < 0) + return -1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + tail = NLMSG_TAIL(n); + + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)); + + if (dist_size > 0) { + addattr_l(n, 32768, TCA_NETEM_DELAY_DIST, + dist_data, dist_size*sizeof(dist_data[0])); + } + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + const struct tc_netem_corr *cor = NULL; + struct tc_netem_qopt qopt; + int len = RTA_PAYLOAD(opt) - sizeof(qopt); + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + if (len < 0) { + fprintf(stderr, "options size error\n"); + return -1; + } + memcpy(&qopt, RTA_DATA(opt), sizeof(qopt)); + + if (len > 0) { + struct rtattr *tb[TCA_NETEM_MAX+1]; + parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt), + len); + + if (tb[TCA_NETEM_CORR]) { + if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor)) + return -1; + cor = RTA_DATA(tb[TCA_NETEM_CORR]); + } + } + + fprintf(f, "limit %d", qopt.limit); + + if (qopt.latency) { + fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1)); + + if (qopt.jitter) { + fprintf(f, " %s", sprint_ticks(qopt.jitter, b1)); + if (cor && cor->delay_corr) + fprintf(f, " %s", sprint_percent(cor->delay_corr, b1)); + } + } + + if (qopt.loss) { + fprintf(f, " loss %s", sprint_percent(qopt.loss, b1)); + if (cor && cor->loss_corr) + fprintf(f, " %s", sprint_percent(cor->loss_corr, b1)); + } + + if (qopt.duplicate) { + fprintf(f, " duplicate %s", + sprint_percent(qopt.duplicate, b1)); + if (cor && cor->dup_corr) + fprintf(f, " %s", sprint_percent(cor->dup_corr, b1)); + } + + if (qopt.gap) + fprintf(f, " gap %lu", (unsigned long)qopt.gap); + + return 0; +} + +struct qdisc_util netem_qdisc_util = { + .id = "netem", + .parse_qopt = netem_parse_opt, + .print_qopt = netem_print_opt, +}; + diff --git a/tc/q_netem.so b/tc/q_netem.so new file mode 100755 index 0000000000000000000000000000000000000000..b312deac1e9f209391c464cf674a2d2ffd2b82f6 GIT binary patch literal 12731 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 LVo{@xnA86T!F+J_ literal 0 HcmV?d00001 diff --git a/tc/q_prio.c b/tc/q_prio.c new file mode 100644 index 0000000..d696e1b --- /dev/null +++ b/tc/q_prio.c @@ -0,0 +1,119 @@ +/* + * q_prio.c PRIO. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Changes: + * + * Ole Husgaard <sparre@login.dknet.dk>: 990513: prio2band map was always reset. + * J Hadi Salim <hadi@cyberus.ca>: 990609: priomap fix. + */ + +#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" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...\n"); +} + +#define usage() return(-1) + +static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + int pmap_mode = 0; + int idx = 0; + struct tc_prio_qopt opt={3,{ 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }}; + + while (argc > 0) { + if (strcmp(*argv, "bands") == 0) { + if (pmap_mode) + explain(); + NEXT_ARG(); + if (get_integer(&opt.bands, *argv, 10)) { + fprintf(stderr, "Illegal \"bands\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "priomap") == 0) { + if (pmap_mode) { + fprintf(stderr, "Error: duplicate priomap\n"); + return -1; + } + pmap_mode = 1; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + unsigned band; + if (!pmap_mode) { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + if (get_unsigned(&band, *argv, 10)) { + fprintf(stderr, "Illegal \"priomap\" element\n"); + return -1; + } + if (band > opt.bands) { + fprintf(stderr, "\"priomap\" element is out of bands\n"); + return -1; + } + if (idx > TC_PRIO_MAX) { + fprintf(stderr, "\"priomap\" index > TC_PRIO_MAX=%u\n", TC_PRIO_MAX); + return -1; + } + opt.priomap[idx++] = band; + } + argc--; argv++; + } + +/* + if (pmap_mode) { + for (; idx < TC_PRIO_MAX; idx++) + opt.priomap[idx] = opt.priomap[TC_PRIO_BESTEFFORT]; + } +*/ + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + int i; + struct tc_prio_qopt *qopt; + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + fprintf(f, "bands %u priomap ", qopt->bands); + for (i=0; i<=TC_PRIO_MAX; i++) + fprintf(f, " %d", qopt->priomap[i]); + return 0; +} + +struct qdisc_util prio_qdisc_util = { + .id = "prio", + .parse_qopt = prio_parse_opt, + .print_qopt = prio_print_opt, +}; + diff --git a/tc/q_prio.o b/tc/q_prio.o new file mode 100644 index 0000000000000000000000000000000000000000..addc84da27b1b6039270f7d9042b8f6a8bbe506e GIT binary patch literal 4000 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 ChLjNi literal 0 HcmV?d00001 diff --git a/tc/q_red.c b/tc/q_red.c new file mode 100644 index 0000000..1743f6c --- /dev/null +++ b/tc/q_red.c @@ -0,0 +1,219 @@ +/* + * q_red.c RED. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 "tc_red.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... red limit BYTES min BYTES max BYTES avpkt BYTES burst PACKETS\n"); + fprintf(stderr, " probability PROBABILITY bandwidth KBPS [ ecn ]\n"); +} + +#define usage() return(-1) + +static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_red_qopt opt; + unsigned burst = 0; + unsigned avpkt = 0; + 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_size(&opt.limit, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "min") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_min, *argv)) { + fprintf(stderr, "Illegal \"min\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "max") == 0) { + NEXT_ARG(); + if (get_size(&opt.qth_max, *argv)) { + fprintf(stderr, "Illegal \"max\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "burst") == 0) { + NEXT_ARG(); + if (get_unsigned(&burst, *argv, 0)) { + fprintf(stderr, "Illegal \"burst\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "avpkt") == 0) { + NEXT_ARG(); + if (get_size(&avpkt, *argv)) { + fprintf(stderr, "Illegal \"avpkt\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "probability") == 0) { + NEXT_ARG(); + if (sscanf(*argv, "%lg", &probability) != 1) { + fprintf(stderr, "Illegal \"probability\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "bandwidth") == 0) { + NEXT_ARG(); + if (get_rate(&rate, *argv)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "ecn") == 0) { + ecn_ok = 1; + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (rate == 0) + get_rate(&rate, "10Mbit"); + + if (!opt.qth_min || !opt.qth_max || !burst || !opt.limit || !avpkt) { + fprintf(stderr, "Required parameter (min, max, burst, limit, avpket) is missing\n"); + return -1; + } + + if ((wlog = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { + fprintf(stderr, "RED: failed to calculate EWMA constant.\n"); + return -1; + } + if (wlog >= 10) + fprintf(stderr, "RED: WARNING. Burst %d seems to be to large.\n", burst); + opt.Wlog = wlog; + if ((wlog = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { + fprintf(stderr, "RED: failed to calculate probability.\n"); + return -1; + } + opt.Plog = wlog; + if ((wlog = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) { + fprintf(stderr, "RED: failed to calculate idle damping table.\n"); + return -1; + } + opt.Scell_log = wlog; + if (ecn_ok) { +#ifdef TC_RED_ECN + opt.flags |= TC_RED_ECN; +#else + fprintf(stderr, "RED: ECN support is missing in this binary.\n"); + return -1; +#endif + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 1024, TCA_RED_PARMS, &opt, sizeof(opt)); + addattr_l(n, 1024, TCA_RED_STAB, sbuf, 256); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_RED_STAB+1]; + struct tc_red_qopt *qopt; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + SPRINT_BUF(b3); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_RED_STAB, opt); + + if (tb[TCA_RED_PARMS] == NULL) + return -1; + qopt = RTA_DATA(tb[TCA_RED_PARMS]); + if (RTA_PAYLOAD(tb[TCA_RED_PARMS]) < sizeof(*qopt)) + return -1; + fprintf(f, "limit %s min %s max %s ", + sprint_size(qopt->limit, b1), + sprint_size(qopt->qth_min, b2), + sprint_size(qopt->qth_max, b3)); +#ifdef TC_RED_ECN + if (qopt->flags & TC_RED_ECN) + fprintf(f, "ecn "); +#endif + if (show_details) { + fprintf(f, "ewma %u Plog %u Scell_log %u", + qopt->Wlog, qopt->Plog, qopt->Scell_log); + } + return 0; +} + +static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) +{ +#ifdef TC_RED_ECN + struct tc_red_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", + st->marked, st->early, st->pdrop, st->other); + return 0; + +#endif + return 0; +} + + +struct qdisc_util red_qdisc_util = { + .id = "red", + .parse_qopt = red_parse_opt, + .print_qopt = red_print_opt, + .print_xstats = red_print_xstats, +}; diff --git a/tc/q_red.o b/tc/q_red.o new file mode 100644 index 0000000000000000000000000000000000000000..a53d29a216e7e8abf639490563fb3b4859f897e0 GIT binary patch literal 7304 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 literal 0 HcmV?d00001 diff --git a/tc/q_sfq.c b/tc/q_sfq.c new file mode 100644 index 0000000..05385cf --- /dev/null +++ b/tc/q_sfq.c @@ -0,0 +1,107 @@ +/* + * q_sfq.c SFQ. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... sfq [ limit NUMBER ] [ perturb SECS ] [ quantum BYTES ]\n"); +} + +#define usage() return(-1) + +static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_sfq_qopt opt; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (strcmp(*argv, "quantum") == 0) { + NEXT_ARG(); + if (get_size(&opt.quantum, *argv)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "perturb") == 0) { + NEXT_ARG(); + if (get_integer(&opt.perturb_period, *argv, 0)) { + fprintf(stderr, "Illegal \"perturb\"\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "limit") == 0) { + NEXT_ARG(); + if (get_u32(&opt.limit, *argv, 0)) { + fprintf(stderr, "Illegal \"limit\"\n"); + return -1; + } + if (opt.limit < 2) { + fprintf(stderr, "Illegal \"limit\", must be > 1\n"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (ok) + addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)); + return 0; +} + +static int sfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct tc_sfq_qopt *qopt; + SPRINT_BUF(b1); + + if (opt == NULL) + return 0; + + if (RTA_PAYLOAD(opt) < sizeof(*qopt)) + return -1; + qopt = RTA_DATA(opt); + fprintf(f, "limit %up ", qopt->limit); + fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1)); + if (show_details) { + fprintf(f, "flows %u/%u ", qopt->flows, qopt->divisor); + } + if (qopt->perturb_period) + fprintf(f, "perturb %dsec ", qopt->perturb_period); + return 0; +} + +struct qdisc_util sfq_qdisc_util = { + .id = "sfq", + .parse_qopt = sfq_parse_opt, + .print_qopt = sfq_print_opt, +}; diff --git a/tc/q_sfq.o b/tc/q_sfq.o new file mode 100644 index 0000000000000000000000000000000000000000..021c8c939263cd76c4e5a205c3e904511a4b5c29 GIT binary patch literal 4056 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 F&%Yy9p2GkD literal 0 HcmV?d00001 diff --git a/tc/q_tbf.c b/tc/q_tbf.c new file mode 100644 index 0000000..6ed5e0b --- /dev/null +++ b/tc/q_tbf.c @@ -0,0 +1,264 @@ +/* + * q_tbf.c TBF. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n"); + fprintf(stderr, " [ peakrate KBPS ] [ latency TIME ]\n"); +} + +static void explain1(char *arg) +{ + fprintf(stderr, "Illegal \"%s\"\n", arg); +} + + +#define usage() return(-1) + +static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + int ok=0; + struct tc_tbf_qopt opt; + __u32 rtab[256]; + __u32 ptab[256]; + unsigned buffer=0, mtu=0, mpu=0, latency=0; + int Rcell_log=-1, Pcell_log = -1; + struct rtattr *tail; + + memset(&opt, 0, sizeof(opt)); + + while (argc > 0) { + if (matches(*argv, "limit") == 0) { + NEXT_ARG(); + if (opt.limit || latency) { + fprintf(stderr, "Double \"limit/latency\" spec\n"); + return -1; + } + if (get_size(&opt.limit, *argv)) { + explain1("limit"); + return -1; + } + ok++; + } else if (matches(*argv, "latency") == 0) { + NEXT_ARG(); + if (opt.limit || latency) { + fprintf(stderr, "Double \"limit/latency\" spec\n"); + return -1; + } + if (get_usecs(&latency, *argv)) { + explain1("latency"); + return -1; + } + ok++; + } else if (matches(*argv, "burst") == 0 || + strcmp(*argv, "buffer") == 0 || + strcmp(*argv, "maxburst") == 0) { + NEXT_ARG(); + if (buffer) { + fprintf(stderr, "Double \"buffer/burst\" spec\n"); + return -1; + } + if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { + explain1("buffer"); + return -1; + } + ok++; + } else if (strcmp(*argv, "mtu") == 0 || + strcmp(*argv, "minburst") == 0) { + NEXT_ARG(); + if (mtu) { + fprintf(stderr, "Double \"mtu/minburst\" spec\n"); + return -1; + } + if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { + explain1("mtu"); + return -1; + } + ok++; + } else if (strcmp(*argv, "mpu") == 0) { + NEXT_ARG(); + if (mpu) { + fprintf(stderr, "Double \"mpu\" spec\n"); + return -1; + } + if (get_size(&mpu, *argv)) { + explain1("mpu"); + return -1; + } + ok++; + } else if (strcmp(*argv, "rate") == 0) { + NEXT_ARG(); + if (opt.rate.rate) { + fprintf(stderr, "Double \"rate\" spec\n"); + return -1; + } + if (get_rate(&opt.rate.rate, *argv)) { + explain1("rate"); + return -1; + } + ok++; + } else if (matches(*argv, "peakrate") == 0) { + NEXT_ARG(); + if (opt.peakrate.rate) { + fprintf(stderr, "Double \"peakrate\" spec\n"); + return -1; + } + if (get_rate(&opt.peakrate.rate, *argv)) { + explain1("peakrate"); + return -1; + } + ok++; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!ok) + return 0; + + if (opt.rate.rate == 0 || !buffer) { + fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n"); + return -1; + } + if (opt.peakrate.rate) { + if (!mtu) { + fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); + return -1; + } + } + + if (opt.limit == 0 && latency == 0) { + fprintf(stderr, "Either \"limit\" or \"latency\" are required.\n"); + return -1; + } + + if (opt.limit == 0) { + double lim = opt.rate.rate*(double)latency/1000000 + buffer; + if (opt.peakrate.rate) { + double lim2 = opt.peakrate.rate*(double)latency/1000000 + mtu; + if (lim2 < lim) + lim = lim2; + } + opt.limit = lim; + } + + if ((Rcell_log = tc_calc_rtable(opt.rate.rate, rtab, Rcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate rate table.\n"); + return -1; + } + opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer); + opt.rate.cell_log = Rcell_log; + opt.rate.mpu = mpu; + if (opt.peakrate.rate) { + if ((Pcell_log = tc_calc_rtable(opt.peakrate.rate, ptab, Pcell_log, mtu, mpu)) < 0) { + fprintf(stderr, "TBF: failed to calculate peak rate table.\n"); + return -1; + } + opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu); + opt.peakrate.cell_log = Pcell_log; + opt.peakrate.mpu = mpu; + } + + tail = NLMSG_TAIL(n); + addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); + addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt)); + addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024); + if (opt.peakrate.rate) + addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024); + tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; + return 0; +} + +static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_TBF_PTAB+1]; + struct tc_tbf_qopt *qopt; + double buffer, mtu; + double latency; + SPRINT_BUF(b1); + SPRINT_BUF(b2); + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_TBF_PTAB, opt); + + if (tb[TCA_TBF_PARMS] == NULL) + return -1; + + qopt = RTA_DATA(tb[TCA_TBF_PARMS]); + if (RTA_PAYLOAD(tb[TCA_TBF_PARMS]) < sizeof(*qopt)) + return -1; + fprintf(f, "rate %s ", sprint_rate(qopt->rate.rate, b1)); + buffer = ((double)qopt->rate.rate*tc_core_tick2usec(qopt->buffer))/1000000; + if (show_details) { + fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1), + 1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2)); + } else { + fprintf(f, "burst %s ", sprint_size(buffer, b1)); + } + if (show_raw) + fprintf(f, "[%08x] ", qopt->buffer); + if (qopt->peakrate.rate) { + fprintf(f, "peakrate %s ", sprint_rate(qopt->peakrate.rate, b1)); + if (qopt->mtu || qopt->peakrate.mpu) { + mtu = ((double)qopt->peakrate.rate*tc_core_tick2usec(qopt->mtu))/1000000; + if (show_details) { + fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1), + 1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2)); + } else { + fprintf(f, "minburst %s ", sprint_size(mtu, b1)); + } + if (show_raw) + fprintf(f, "[%08x] ", qopt->mtu); + } + } + + if (show_raw) + fprintf(f, "limit %s ", sprint_size(qopt->limit, b1)); + + latency = 1000000*(qopt->limit/(double)qopt->rate.rate) - tc_core_tick2usec(qopt->buffer); + if (qopt->peakrate.rate) { + double lat2 = 1000000*(qopt->limit/(double)qopt->peakrate.rate) - tc_core_tick2usec(qopt->mtu); + if (lat2 > latency) + latency = lat2; + } + fprintf(f, "lat %s ", sprint_usecs(tc_core_tick2usec(latency), b1)); + + return 0; +} + +struct qdisc_util tbf_qdisc_util = { + .id = "tbf", + .parse_qopt = tbf_parse_opt, + .print_qopt = tbf_print_opt, +}; + diff --git a/tc/q_tbf.o b/tc/q_tbf.o new file mode 100644 index 0000000000000000000000000000000000000000..b060ba68e3864a8675b9e29ce4c7efce506a0cbe GIT binary patch literal 9552 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 VMUyqzaM@mM!`F%Sb{j7B{|m)ot%d*q literal 0 HcmV?d00001 diff --git a/tc/tc b/tc/tc new file mode 100755 index 0000000000000000000000000000000000000000..0962fc70fb9dad870400f7cf4dcac012cf3b4d6e GIT binary patch literal 189642 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 Bh1mcA literal 0 HcmV?d00001 diff --git a/tc/tc.c b/tc/tc.c new file mode 100644 index 0000000..dd6ac97 --- /dev/null +++ b/tc/tc.c @@ -0,0 +1,347 @@ +/* + * tc.c "tc" utility frontend. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Fixes: + * + * Petri Mattila <petri@prihateam.fi> 990308: wrong memset's resulted in faults + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <errno.h> + +#include "SNAPSHOT.h" +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +int show_stats = 0; +int show_details = 0; +int show_raw = 0; +int resolve_hosts = 0; +int use_iec = 0; +struct rtnl_handle rth; + +static void *BODY; /* cached handle dlopen(NULL) */ +static struct qdisc_util * qdisc_list; +static struct filter_util * filter_list; + +static int print_noqopt(struct qdisc_util *qu, FILE *f, + struct rtattr *opt) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "[Unknown qdisc, optlen=%u] ", + (unsigned) RTA_PAYLOAD(opt)); + return 0; +} + +static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +{ + if (argc) { + fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); + return -1; + } + return 0; +} + +static int print_nofopt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle) +{ + if (opt && RTA_PAYLOAD(opt)) + fprintf(f, "fh %08x [Unknown filter, optlen=%u] ", + fhandle, (unsigned) RTA_PAYLOAD(opt)); + else if (fhandle) + fprintf(f, "fh %08x ", fhandle); + return 0; +} + +static int parse_nofopt(struct filter_util *qu, char *fhandle, int argc, char **argv, struct nlmsghdr *n) +{ + __u32 handle; + + if (argc) { + fprintf(stderr, "Unknown filter \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); + return -1; + } + if (fhandle) { + struct tcmsg *t = NLMSG_DATA(n); + if (get_u32(&handle, fhandle, 16)) { + fprintf(stderr, "Unparsable filter ID \"%s\"\n", fhandle); + return -1; + } + t->tcm_handle = handle; + } + return 0; +} + +struct qdisc_util *get_qdisc_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct qdisc_util *q; + + for (q = qdisc_list; q; q = q->next) + if (strcmp(q->id, str) == 0) + return q; + + snprintf(buf, sizeof(buf), "/usr/lib/tc/q_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (!dlh) { + /* look in current binary, only open once */ + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_qdisc_util", str); + q = dlsym(dlh, buf); + if (q == NULL) + goto noexist; + +reg: + q->next = qdisc_list; + qdisc_list = q; + return q; + +noexist: + q = malloc(sizeof(*q)); + if (q) { + + memset(q, 0, sizeof(*q)); + q->id = strcpy(malloc(strlen(str)+1), str); + q->parse_qopt = parse_noqopt; + q->print_qopt = print_noqopt; + goto reg; + } + return q; +} + + +struct filter_util *get_filter_kind(const char *str) +{ + void *dlh; + char buf[256]; + struct filter_util *q; + + for (q = filter_list; q; q = q->next) + if (strcmp(q->id, str) == 0) + return q; + + snprintf(buf, sizeof(buf), "/usr/lib/tc/f_%s.so", str); + dlh = dlopen(buf, RTLD_LAZY); + if (dlh == NULL) { + dlh = BODY; + if (dlh == NULL) { + dlh = BODY = dlopen(NULL, RTLD_LAZY); + if (dlh == NULL) + goto noexist; + } + } + + snprintf(buf, sizeof(buf), "%s_filter_util", str); + q = dlsym(dlh, buf); + if (q == NULL) + goto noexist; + +reg: + q->next = filter_list; + filter_list = q; + return q; +noexist: + q = malloc(sizeof(*q)); + if (q) { + memset(q, 0, sizeof(*q)); + strncpy(q->id, str, 15); + q->parse_fopt = parse_nofopt; + q->print_fopt = print_nofopt; + goto reg; + } + return q; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { qdisc | class | filter | action }\n" + " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -b[atch] file }\n"); +} + +static int do_cmd(int argc, char **argv) +{ + if (matches(*argv, "qdisc") == 0) + return do_qdisc(argc-1, argv+1); + + if (matches(*argv, "class") == 0) + return do_class(argc-1, argv+1); + + if (matches(*argv, "filter") == 0) + return do_filter(argc-1, argv+1); + + if (matches(*argv, "actions") == 0) + return do_action(argc-1, argv+1); + + if (matches(*argv, "help") == 0) { + usage(); + return 0; + } + + fprintf(stderr, "Object \"%s\" is unknown, try \"tc help\".\n", + *argv); + return -1; +} + +static int makeargs(char *line, char *argv[], int maxargs) +{ + static const char ws[] = " \t\r\n"; + char *cp; + int argc = 0; + + for (cp = strtok(line, ws); cp; cp = strtok(NULL, ws)) { + if (argc >= maxargs) { + fprintf(stderr, "Too many arguments to command\n"); + exit(1); + } + argv[argc++] = cp; + } + argv[argc] = NULL; + + return argc; +} + +static int batch(const char *name) +{ + char *line = NULL; + size_t len = 0; + ssize_t cc; + int lineno = 0; + char *largv[100]; + int largc, ret = 0; + + if (strcmp(name, "-") != 0) { + if (freopen(name, "r", stdin) == NULL) { + fprintf(stderr, "Cannot open file \"%s\" for reading: %s=n", + name, strerror(errno)); + return -1; + } + } + + tc_core_init(); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return -1; + } + + while ((cc = getline(&line, &len, stdin)) != -1) { + ++lineno; + + /* ignore blank lines and comments */ + if (*line == '\n' || *line == '#') + continue; + + /* handle continuation lines */ + while (cc >= 2 && strcmp(line+cc-2, "\\\n") == 0) { + char *line1 = NULL; + ssize_t len1 = 0; + int cc1; + cc1 = getline(&line1, &len1, stdin); + + if (cc1 < 0) { + fprintf(stderr, "Missing continuation line\n"); + return -1; + } + ++lineno; + line = realloc(line, cc + cc1); + if (!line) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + + strcpy(line+cc-2, line1); + cc += cc1 - 2; + free(line1); + } + + largc = makeargs(line, largv, 100); + + ret = do_cmd(largc, largv); + if (ret) { + fprintf(stderr, "Command failed %s:%d\n", name, lineno); + break; + } + } + + rtnl_close(&rth); + return ret; +} + + +int main(int argc, char **argv) +{ + int ret; + + while (argc > 1) { + if (argv[1][0] != '-') + break; + if (matches(argv[1], "-stats") == 0 || + matches(argv[1], "-statistics") == 0) { + ++show_stats; + } else if (matches(argv[1], "-details") == 0) { + ++show_details; + } else if (matches(argv[1], "-raw") == 0) { + ++show_raw; + } else if (matches(argv[1], "-Version") == 0) { + printf("tc utility, iproute2-ss%s\n", SNAPSHOT); + return 0; + } else if (matches(argv[1], "-iec") == 0) { + ++use_iec; + } else if (matches(argv[1], "-help") == 0) { + usage(); + return 0; + } else if (matches(argv[1], "-batch") == 0) { + if (argc < 3) { + fprintf(stderr, "Wrong number of arguments in batch mode\n"); + return -1; + } + + return batch(argv[2]); + } else { + fprintf(stderr, "Option \"%s\" is unknown, try \"tc -help\".\n", argv[1]); + return -1; + } + argc--; argv++; + } + + if (argc <= 1) { + usage(); + return 0; + } + + tc_core_init(); + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + exit(1); + } + + ret = do_cmd(argc-1, argv+1); + rtnl_close(&rth); + + return ret; +} diff --git a/tc/tc.o b/tc/tc.o new file mode 100644 index 0000000000000000000000000000000000000000..86a511c6a503300c529ebd8e90839d136ba027dd GIT binary patch literal 10544 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 LF6Sv+{e9}+b9fM~ literal 0 HcmV?d00001 diff --git a/tc/tc_cbq.c b/tc/tc_cbq.c new file mode 100644 index 0000000..0abcc9d --- /dev/null +++ b/tc/tc_cbq.c @@ -0,0 +1,57 @@ +/* + * tc_cbq.c CBQ maintanance routines. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" +#include "tc_cbq.h" + +unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned maxburst) +{ + double maxidle; + double g = 1.0 - 1.0/(1<<ewma_log); + double xmt = (double)avpkt/bndw; + + maxidle = xmt*(1-g); + if (bndw != rate && maxburst) { + double vxmt = (double)avpkt/rate - xmt; + vxmt *= (pow(g, -(double)maxburst) - 1); + if (vxmt > maxidle) + maxidle = vxmt; + } + return tc_core_usec2tick(maxidle*(1<<ewma_log)*1000000); +} + +unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned minburst) +{ + double g = 1.0 - 1.0/(1<<ewma_log); + double offtime = (double)avpkt/rate - (double)avpkt/bndw; + + if (minburst == 0) + return 0; + if (minburst == 1) + offtime *= pow(g, -(double)minburst) - 1; + else + offtime *= 1 + (pow(g, -(double)(minburst-1)) - 1)/(1-g); + return tc_core_usec2tick(offtime*1000000); +} diff --git a/tc/tc_cbq.h b/tc/tc_cbq.h new file mode 100644 index 0000000..8f95649 --- /dev/null +++ b/tc/tc_cbq.h @@ -0,0 +1,9 @@ +#ifndef _TC_CBQ_H_ +#define _TC_CBQ_H_ 1 + +unsigned tc_cbq_calc_maxidle(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned maxburst); +unsigned tc_cbq_calc_offtime(unsigned bndw, unsigned rate, unsigned avpkt, + int ewma_log, unsigned minburst); + +#endif diff --git a/tc/tc_cbq.o b/tc/tc_cbq.o new file mode 100644 index 0000000000000000000000000000000000000000..660babbeb9e294c693bd55886e0ee22cd9901dd4 GIT binary patch literal 2728 zcmbtVOKTKa6uzC-`0gr2!ABel3_}dD=uU!0@zEhZA~fRoK*f+wrBXEp)3$b(7*~qX zMJ)scUAXZl%-Wsplc)<Hagm5ZyVDsL+6yOxLgt)X_jDhwErMS9);ae(?{go0ZbxQk zLo6m}#KcxnTM;V6K)lAMC7Turu|V8H<^AE4iE{Y7Vzice$39r$v~Mg~gi0I#t@IkL zZ;k7BD~1Vj^F7R|iqZcJ@lvX^<5J1EUb+P0{&L0W`y1w)igDz1a47tUD$k|f_OOI> zykabFYL?c9`M@`pKJtx2G7gx%1mS^Z^BsnT<!q#o20-vdt0T`UM)p-WB{QCYXssmx z@4=M15TI>ZTKA6Kw~pTPeg6vlq&5`Q6~34#gQsQaH~fs&;S3b>7!!LX3xUKSL-USA zjlOY|JcO(=%`M121^Fa0SS%>@O$I~VW7Dy~?Y^9z0oU6>PZkI|<GebV6)31uv5|e~ z-~bx*gS*3bsK!YsXEmk>f!e-MIbBet`M4TF_JlVNx=JxX2A=Q!^A|d)jq^2Da@zXi zHbl=~w7aEL)YeNQ0M1lw)Lb}c?iF>ongGX1V@feQ6XsaMo<tI5n@I@tKgI7(q$6%u zPtPW+W!IjAYpo8u({8uglgZ9hGPS{K+2;*ey-pC}>(<-8+p$&%yLdJiID<fgLPU=d zSMWw0JMcyWVGlWhBkaLqQP_n%E^N0Lq}KCQ7c9`gJC^kePR<kdiG1MMkjlDZ;JC+y z?dEei??j+d{v~38HMXo#^O9EUDb-IBo5U!(3g|2i!)4372;8ge7s3+Rxe+XuEBd>_ z+4Yx_rUs>;5j|EL$kFG|+NZ!4^I8Iv4@<?jI0!t|PZSSkJ^S=`?5TdDe<*vs+yMm~ z*5Z>L!5Lumo}g@Wex#9%h`|2Ofl2n%|D%AZEmE2hxuqwhq`L0$AJ}7m`OxhbgHYc) z)PJwy>xW#o$926_%j!Uvl>xqOxMzC(cuecXFDb=8LC%LBpUXIpdb7l*8IBPjuiJ6V zqh2P6*V|#l<v$C#t<v~6w>c8U<q4>&RhhU@m$ixr5gi}>RS?3EI^V{P-r>e-f63Jf z$uw5)4dK5jKXi`>U!(Ntd=ic{uCn47JiKssYr8`4{1(7<-)PtU9eh9F=%)-62mK$2 zz|FdYr)*c)nVt^(r0~;;A9$!UJ@9k`mQ;FK*BNoMIcIcuXv7mI^JgTUFL>G0MbB*y zhT-{+JVu&%-w%f2i5DxQu<|V9ZhW1I#s!Aso)CVO;dqA#zrk?s=K;ePF#V?t$2XAt zd}27h9fS|4R^y$+J4Lw1aGp<&;oQ$Ph9{Vxn+)gq+-5kB`-tH@t_41EpqRf8X#`;q JUY08%{s(MZpnU)U literal 0 HcmV?d00001 diff --git a/tc/tc_class.c b/tc/tc_class.c new file mode 100644 index 0000000..c4b27eb --- /dev/null +++ b/tc/tc_class.c @@ -0,0 +1,319 @@ +/* + * tc_class.c "tc class". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 <math.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void usage(void); + +static void usage(void) +{ + fprintf(stderr, "Usage: tc class [ add | del | change | get ] dev STRING\n"); + fprintf(stderr, " [ classid CLASSID ] [ root | parent CLASSID ]\n"); + fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc class show [ dev STRING ] [ root | parent CLASSID ]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n"); + fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n"); + return; +} + +int tc_class_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[4096]; + } req; + struct qdisc_util *q = NULL; + struct tc_estimator est; + char d[16]; + char k[16]; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(d, 0, sizeof(d)); + memset(k, 0, sizeof(k)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "classid") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_handle) + duparg("classid", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid class ID"); + req.t.tcm_handle = handle; + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + req.t.tcm_parent = handle; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est)) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_qdisc_kind(k); + argc--; argv++; + break; + } + argc--; argv++; + } + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + if (q) { + if (q->parse_copt == NULL) { + fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k); + return 1; + } + if (q->parse_copt(q, argc, argv, &req.n)) + return 1; + } else { + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv); + return -1; + } + } + + if (d[0]) { + ll_init_map(&rth); + + if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + +int filter_ifindex; +__u32 filter_qdisc; + +static int print_class(const struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct qdisc_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) { + fprintf(stderr, "Not a class\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc)) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_class: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELTCLASS) + fprintf(fp, "deleted "); + + abuf[0] = 0; + if (t->tcm_handle) { + if (filter_qdisc) + print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle)); + else + print_tc_classid(abuf, sizeof(abuf), t->tcm_handle); + } + fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf); + + if (filter_ifindex == 0) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else { + if (filter_qdisc) + print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent)); + else + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + if (t->tcm_info) + fprintf(fp, "leaf %x: ", t->tcm_info>>16); + q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); + if (tb[TCA_OPTIONS]) { + if (q && q->print_copt) + q->print_copt(q, fp, tb[TCA_OPTIONS]); + else + fprintf(fp, "[cannot parse class parameters]"); + } + fprintf(fp, "\n"); + if (show_stats) { + struct rtattr *xstats = NULL; + + if (tb[TCA_STATS] || tb[TCA_STATS2]) { + print_tcstats_attr(fp, tb, " ", &xstats); + fprintf(fp, "\n"); + } + if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) { + q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]); + fprintf(fp, "\n"); + } + } + fflush(fp); + return 0; +} + + +int tc_class_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "qdisc") == 0) { + NEXT_ARG(); + if (filter_qdisc) + duparg("qdisc", *argv); + if (get_qdisc_handle(&filter_qdisc, *argv)) + invarg(*argv, "invalid qdisc ID"); + } else if (strcmp(*argv, "root") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + if (t.tcm_parent) + duparg("parent", *argv); + NEXT_ARG(); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + t.tcm_parent = handle; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv); + return -1; + } + + argc--; argv++; + } + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_class(int argc, char **argv) +{ + if (argc < 1) + return tc_class_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_class_modify(RTM_DELTCLASS, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_class_get(RTM_GETTCLASS, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_class_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv); + return -1; +} diff --git a/tc/tc_class.o b/tc/tc_class.o new file mode 100644 index 0000000000000000000000000000000000000000..77d3dc15dea1ef2f05dd42fcba76e7f105d88dc6 GIT binary patch literal 11568 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 literal 0 HcmV?d00001 diff --git a/tc/tc_common.h b/tc/tc_common.h new file mode 100644 index 0000000..7e13582 --- /dev/null +++ b/tc/tc_common.h @@ -0,0 +1,11 @@ + +#define TCA_BUF_MAX (64*1024) + +extern struct rtnl_handle rth; +extern int do_qdisc(int argc, char **argv); +extern int do_class(int argc, char **argv); +extern int do_filter(int argc, char **argv); +extern int do_action(int argc, char **argv); + +struct tc_estimator; +extern int parse_estimator(int *p_argc, char ***p_argv, struct tc_estimator *est); diff --git a/tc/tc_core.c b/tc/tc_core.c new file mode 100644 index 0000000..07cf2fa --- /dev/null +++ b/tc/tc_core.c @@ -0,0 +1,89 @@ +/* + * tc_core.c TC core library. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" + +static __u32 t2us=1; +static __u32 us2t=1; +static double tick_in_usec = 1; + +long tc_core_usec2tick(long usec) +{ + return usec*tick_in_usec; +} + +long tc_core_tick2usec(long tick) +{ + return tick/tick_in_usec; +} + +unsigned tc_calc_xmittime(unsigned rate, unsigned size) +{ + return tc_core_usec2tick(1000000*((double)size/rate)); +} + +/* + rtab[pkt_len>>cell_log] = pkt_xmit_time + */ + +int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, + unsigned mpu) +{ + int i; + unsigned overhead = (mpu >> 8) & 0xFF; + mpu = mpu & 0xFF; + + if (mtu == 0) + mtu = 2047; + + if (cell_log < 0) { + cell_log = 0; + while ((mtu>>cell_log) > 255) + cell_log++; + } + for (i=0; i<256; i++) { + unsigned sz = (i<<cell_log); + if (overhead) + sz += overhead; + if (sz < mpu) + sz = mpu; + rtab[i] = tc_core_usec2tick(1000000*((double)sz/bps)); + } + return cell_log; +} + +int tc_core_init() +{ + FILE *fp = fopen("/proc/net/psched", "r"); + + if (fp == NULL) + return -1; + + if (fscanf(fp, "%08x%08x", &t2us, &us2t) != 2) { + fclose(fp); + return -1; + } + fclose(fp); + tick_in_usec = (double)t2us/us2t; + return 0; +} diff --git a/tc/tc_core.h b/tc/tc_core.h new file mode 100644 index 0000000..1537f95 --- /dev/null +++ b/tc/tc_core.h @@ -0,0 +1,19 @@ +#ifndef _TC_CORE_H_ +#define _TC_CORE_H_ 1 + +#include <asm/types.h> +#include <linux/pkt_sched.h> + +long tc_core_usec2tick(long usec); +long tc_core_tick2usec(long tick); +unsigned tc_calc_xmittime(unsigned rate, unsigned size); +int tc_calc_rtable(unsigned bps, __u32 *rtab, int cell_log, unsigned mtu, unsigned mpu); + +int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est); + +int tc_core_init(void); + +extern struct rtnl_handle g_rth; +extern int is_batch_mode; + +#endif diff --git a/tc/tc_core.o b/tc/tc_core.o new file mode 100644 index 0000000000000000000000000000000000000000..9b7d80e6a851d01c85bf27b83a7d8f914d99b05f GIT binary patch literal 3080 zcmbVOU1%It6h50xO=|1zwpIzKFi6xSw3F;6XcVnG$;OPb7HOM`q|J6anI*B=-LN~s z_@Fhc5ytuxUwsnv!56_NN&BFyiLDC7O2HQq^Wa0Q7260Qh-JOs%stuL*#yDE?%Z>} z^PO|g{hK>yhSE<pg+jy<qJvbo7$y23T-TGHnxqKrqBS$xb#X2F!VX}<*k4`B<#G#~ zKj|Oa&hM_fHE_Pi<HA~W>`OJ-#wybOrKB{TH;x(ZkM4KIh;ektT~50z#@w$p!5WEv zwrsc;7i%p<N6gB#*!;KA)a9Z1b<=g)TW3p#do%t=+Feb%S1T(a^M|YE&aX^&U%R`S za&MT{&sSE~o{n~1N`*F3kFFTef#urQZWUEkx30=Od$hfE?k2i+o8NzR(X1@TQttK9 z6UK4lgfV7hmWJZ%bAQACd(^01a4(B~uB<)*;H5u*%hzf(F-u227bbmyhc{N!U0M{L znmPZI^Gv*Y4vzD9cs$en#bZ@Anr3u1&hp%LRqN_Eaar~r&pW!r>`^cM5>gUfe-dlW z>;2m6x&ahziMpptMXS4DJKfV|Yr-C<2YUKv@dt(Wc~8WKW@EcsS{Gyz;P*-&nmeTq zyie+AR6MQV4@&)U(T*$lC8-ZYVgr%3cf#*B4M((rNXJ_{-flh==}AZWQxUV$^2TkF zhZ;Uo;{B?Lj7Y4~1bldK@G-4p_|QnF)~omF2`$mn(--fFKcaOUw#PLy>-hMAhxMG* z+exICU(P$(Q=)ZBemlWh$)3vUjy>y;KAv^5q@OC6NiP*)=vLY3-!w!-T#pMA5%xqT zSIXvX(hEh$)<rD`%1+igO}bUg=k0={)c=1~O$1_TcwRV9QYwqQ_|lFsLALjZwmE>o zT-oV^pUU{%!i3uD!_+2QtS4~g_2GFoWL&U6j+f!IUnszW@vYt>GOOMOYmEyRy4qW( zcpArV*FV>BP{t<$@olnyb+3aWhqc)rmRPXdehE|&qzL!R(6d4bjtRbnT|R^NHRfNu z_!{Q_xWKq3Kf-@*1PhYRAb09N;xWIf1mpJzXK?Mn@08$-8Z7i+JnXGX)i78g9nyJ+ z(4*qd^T+!S+jOh`O}T&H6Mj{K{i{Az2d|lK;AP$*;f7K8l<=07puWZx#`1;~zB(@8 zY;)v&l8=1=VH|pxygLBuEfQ{cEBNlPx07z6dPbP!q$#fZJ-spiLlWmYl@L+S?W*bN zIOltndE@&5=MT8%vFi86a}rNVobQdS#HS_B_Xcd_Ny-K0bMuP8gYN)@R<^<4+y*Dd z%2>scty|<wTBkFUh0ILZwrHlDaL7r_i2EQtC|>;O!Ct0%gap`|2n@j$aY&b)vNE&z zNynKKcLW2td!~F-bSL_mbSPJxwhNRiTiHU6a@JI_Y-0=%UNIaKoP&SxaUrA=P9Z`3 zm=Ak6_7d|?Dmcy=<JS}%XOQuA1y}h4a{jRcxHeFS1Gqfal$JPbd_!>llL{Z>;(cZZ za6S(&DmeP({Z&%(-=*X~qu_Td_`HIv{qpw@&nKV14+DIz{}TmQ^J}6n2;4jeu>0{4 d!no#HL^uZ!#*>~!G$C8Y`8<3az>^>d`4`Qou^s>b literal 0 HcmV?d00001 diff --git a/tc/tc_estimator.c b/tc/tc_estimator.c new file mode 100644 index 0000000..434db0f --- /dev/null +++ b/tc/tc_estimator.c @@ -0,0 +1,44 @@ +/* + * tc_core.c TC core library. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" + +int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est) +{ + for (est->interval=0; est->interval<=5; est->interval++) { + if (A <= (1<<est->interval)*(1000000/4)) + break; + } + if (est->interval > 5) + return -1; + est->interval -= 2; + for (est->ewma_log=1; est->ewma_log<32; est->ewma_log++) { + double w = 1.0 - 1.0/(1<<est->ewma_log); + if (A/(-log(w)) > time_const) + break; + } + est->ewma_log--; + if (est->ewma_log==0 || est->ewma_log >= 31) + return -1; + return 0; +} diff --git a/tc/tc_estimator.o b/tc/tc_estimator.o new file mode 100644 index 0000000000000000000000000000000000000000..c397f73ca965082c267ef2145e47cce1525559bd GIT binary patch literal 1992 zcmbtV%}*0i5P#dHP_^tU1QYy-?tvH}vT1=Jnnt><${LA55i#M*rR<7<Qqx@(FKW`n zw24S>UilL|ao|8OD(S%kLcDvxgcuLl#CXBfH|=}r@>pV`lkU#UZ{|1i-t61$v&(%~ zJc59j2<RfJcnn2I^Hl6e$&4Tg9YN}ldRrY-Zw(G;*-b6|-M8jJ%ei$CnWmW|KfEMn zKjW4B=GXJTEnjgp?9GTzzlFWaMIv}1M@pr)J!>&RP_w%kG5b?pf%bUmns6z*W8(T+ zXtw#KfLm7!cr0JQfwv4T;E|GFxQbgp7I2-vX&aJF+^|Yg{7ohv@&ZueX}I-+iIx20 zTD%k!$U;tMV#1@>;LP_I$r{3^G6TY~jb9sD`n@l^K0K<9sH5tbI{pr7aPoYwztht| z0Rahf0e_7zM~4_D_(@0d7PX^NASyLRB{?cJt5Q&vy3;i;s$Y8Nt6qr<o?a=!IK8ph z1-ZHR`e2LPp>!(ka(gh?848BZ$;~&7Nm<kFGTzpunDLGlgcR#Z!qz8<wo_$2#jKPu zqbs&CXCq}&w{@gUSQb)JNpO_7Wrw<)UN>=oU`&mhDLr8zWj1LW3Ry~<W$W>2q{Nen zgfVNgrT&kW2^mIK%@v7IpUsVh1I(cM1kqI-25x^%8N6Fo$WJu%@ln5#>N6A<sn1js zeU8j$n(uX%1fb@>@<W6Rt?J3;lpyWT@0a;Z_YjgBKSKFzzpOU%nZ7~!j@P`Ds!{$O zMx>hY7~Dq;ekKrmL+8)t4D$zw@fki$FxrY(6<KrxNtlPHT`;J^_%L6m@ccRw<ewen zf1-)}A?Nwv^RokfzVrnm=a3TMa|Scu_kW0BZv7kS{QkCnLU$EtTZMiU-Gbu2DVDQD z*OkEf+@3Jtbdy{Yao^j_>eEf_%ZDlKmGb%KzX}K)Eu|gQ_we2##5+!|ddGgZXhs{+ z>>Z*|S=sS%!?N!ubUT?+;z;R>wZkohNWe1ehYu=YG?To?xD(WXvjDr<h5tDsa|hrH U4jj$~%U^Whup5TU6z9*|A7`i5(f|Me literal 0 HcmV?d00001 diff --git a/tc/tc_filter.c b/tc/tc_filter.c new file mode 100644 index 0000000..f6de840 --- /dev/null +++ b/tc/tc_filter.c @@ -0,0 +1,371 @@ +/* + * tc_filter.c "tc filter". + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <linux/if_ether.h> + +#include "rt_names.h" +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static void usage(void); + +static void usage(void) +{ + fprintf(stderr, "Usage: tc filter [ add | del | change | get ] dev STRING\n"); + fprintf(stderr, " [ pref PRIO ] [ protocol PROTO ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ root | classid CLASSID ] [ handle FILTERID ]\n"); + fprintf(stderr, " [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc filter show [ dev STRING ] [ root | parent CLASSID ]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "FILTER_TYPE := { rsvp | u32 | fw | route | etc. }\n"); + fprintf(stderr, "FILTERID := ... format depends on classifier, see there\n"); + fprintf(stderr, "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n"); + return; +} + + +int tc_filter_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[MAX_MSG]; + } req; + struct filter_util *q = NULL; + __u32 prio = 0; + __u32 protocol = 0; + char *fhandle = NULL; + char d[16]; + char k[16]; + struct tc_estimator est; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(d, 0, sizeof(d)); + memset(k, 0, sizeof(k)); + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "Invalid parent ID"); + req.t.tcm_parent = handle; + } else if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + if (fhandle) + duparg("handle", *argv); + fhandle = *argv; + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (prio) + duparg("priority", *argv); + if (get_u32(&prio, *argv, 0)) + invarg(*argv, "invalid prpriority value"); + } else if (matches(*argv, "protocol") == 0) { + __u16 id; + NEXT_ARG(); + if (protocol) + duparg("protocol", *argv); + if (ll_proto_a2n(&id, *argv)) + invarg(*argv, "invalid protocol"); + protocol = id; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est) < 0) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_filter_kind(k); + argc--; argv++; + break; + } + + argc--; argv++; + } + + req.t.tcm_info = TC_H_MAKE(prio<<16, protocol); + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + + if (q) { + if (q->parse_fopt(q, fhandle, argc, argv, &req.n)) + return 1; + } else { + if (fhandle) { + fprintf(stderr, "Must specify filter type when using " + "\"handle\"\n"); + return -1; + } + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n", *argv); + return -1; + } + } + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + + if (d[0]) { + ll_init_map(&rth); + + if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) { + fprintf(stderr, "We have an error talking to the kernel\n"); + return 2; + } + + return 0; +} + +static __u32 filter_parent; +static int filter_ifindex; +static __u32 filter_prio; +static __u32 filter_protocol; + +static int print_filter(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct filter_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWTFILTER && n->nlmsg_type != RTM_DELTFILTER) { + fprintf(stderr, "Not a filter\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_filter: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELTFILTER) + fprintf(fp, "deleted "); + + fprintf(fp, "filter "); + if (!filter_ifindex || filter_ifindex != t->tcm_ifindex) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + + if (!filter_parent || filter_parent != t->tcm_parent) { + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else { + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + } + if (t->tcm_info) { + __u32 protocol = TC_H_MIN(t->tcm_info); + __u32 prio = TC_H_MAJ(t->tcm_info)>>16; + if (!filter_protocol || filter_protocol != protocol) { + if (protocol) { + SPRINT_BUF(b1); + fprintf(fp, "protocol %s ", + ll_proto_n2a(protocol, b1, sizeof(b1))); + } + } + if (!filter_prio || filter_prio != prio) { + if (prio) + fprintf(fp, "pref %u ", prio); + } + } + fprintf(fp, "%s ", (char*)RTA_DATA(tb[TCA_KIND])); + q = get_filter_kind(RTA_DATA(tb[TCA_KIND])); + if (tb[TCA_OPTIONS]) { + if (q) + q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle); + else + fprintf(fp, "[cannot parse parameters]"); + } + fprintf(fp, "\n"); + + if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) { + print_tcstats_attr(fp, tb, " ", NULL); + fprintf(fp, "\n"); + } + + fflush(fp); + return 0; +} + + +int tc_filter_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + __u32 prio = 0; + __u32 protocol = 0; + char *fhandle = NULL; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "root") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + filter_parent = t.tcm_parent = TC_H_ROOT; + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + filter_parent = t.tcm_parent = handle; + } else if (strcmp(*argv, "handle") == 0) { + NEXT_ARG(); + if (fhandle) + duparg("handle", *argv); + fhandle = *argv; + } else if (matches(*argv, "preference") == 0 || + matches(*argv, "priority") == 0) { + NEXT_ARG(); + if (prio) + duparg("priority", *argv); + if (get_u32(&prio, *argv, 0)) + invarg(*argv, "invalid preference"); + filter_prio = prio; + } else if (matches(*argv, "protocol") == 0) { + __u16 res; + NEXT_ARG(); + if (protocol) + duparg("protocol", *argv); + if (ll_proto_a2n(&res, *argv)) + invarg(*argv, "invalid protocol"); + protocol = res; + filter_protocol = protocol; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, " What is \"%s\"? Try \"tc filter help\"\n", *argv); + return -1; + } + + argc--; argv++; + } + + t.tcm_info = TC_H_MAKE(prio<<16, protocol); + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_filter, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_filter(int argc, char **argv) +{ + if (argc < 1) + return tc_filter_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_filter_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n", *argv); + return -1; +} + diff --git a/tc/tc_filter.o b/tc/tc_filter.o new file mode 100644 index 0000000000000000000000000000000000000000..5924f02f3ff4a137e8abf9ac876c84bfc23120bf GIT binary patch literal 13720 zcmbVS4U|(wmae87Xc3bhL8l$Z@zDlsW+2-Eae(osBWZXU(!fHu=m3V$d7-nWlbF2l z<M3<xIlc{PIjb}3I_v1sqwKCb#~nR_y|d6DE1)wT7I)6#>>S0>Bb`ByxG1vzW$&$5 zmE3-vcjla_BY9O{eRb>JTeqt2d+qj)(84iwbxf8zb`z_%B+A&(<<)vSueLK6yO^o@ z4;t0NC5Q~pgP;94_y9JC@ke&GLfxTjhI7~T&RyPGU2_0r2PVMJh{<oR#kEu(@7y)Z zxofU-*9{}O)coPb5+E!V+im^lU)kX+`)}>=o%eTl_&)S^b(^31&aW`PP<MWCUpr%= zV$`$D?D9+=^gv5BL!Ra%0S~&5h0N33EZ=$F2hA9Cd4L5EAX^nxR@!6*wY3})D1cyd z80gDj7|S26i<zrEU9SW@Ygx<;cy8@5PPhk7L071-+S7byU2M0%{A$QN8O*=j2z?=7 zHwREV_rT}%jP2M0^SX^spsE(qjcO6vgYFTtaY6p#@^Y}axY0O)V}s^v+%CU)V%x@G zVUqjBG5O(W?sE5wx76i_wcI680c1n20V?&zkh_0lxm@ln)DMDWwr?!_e5soMt(pfz z<}2r(b{lgc<_fweVVrjxkAg08I<PYVBg><v-EJ1Zrw30p1unV(HncIYJ*{`E#gL~h zXdd+!STH}-P~2Vq`s=ShJ3i&m&Oh#eqMtU8??*?2=MN4yeRgQa4#wPp<H2J4n2<X# z#7+!i@CWlJ>x0ve`pZLnwK|Ot-ThocFkLOcdUl$pLgx99`H{ce-E0i;LAg6XkE!6x zEn|dR^=s7PYEL`aO6J>KpfLp6M&13-z+}D?9lm27z9HYSkoi8Rsz(}dX|474odECM z=f&mOc^IS1Jicm;f3<&&{|<lnWemvOmQQgTfHQ?#JIy!GJ)H}}($`q`F`yt=kuZ+A zjc%w13wQB_pCOcU8*`8m@XP=;6Q$3-Dwk``0TbL^oj}CY6JSy&uHr%v(OH;XdKpG@ z;1{^W0ncQNh7(_x%PKUQ_oJa|VXmCHe<KXzJ!;`b>=T40pS2W&7!0M4VL0yynMdSE z;~p*vWb!Q%D&E!z%0a|)#dz4e2O3d_E>A1$5u*%s8S<^Z6H-opBPc9p1z}P*p^j?) zp7(Kfbzyk`lz!xBRTQ+YX|M6_pVfLBZ0WMw$Pjg>No9@YfCk)qw|J(NmVH?+^LW5L z;PLMRN3ucOX)@sXpwc=pk9&hR(E(L6<WWBpM}cH=3s{cd1cN1M>S)rLH@S04hrkgS zR8;cl#m>!Di(MX%TGYYKaPCZ&R++{Z5OI8^$HRod!fY;xYF$Ixy5ZUX$30M52*ah8 z*N)b55Is}+7jV}|>xa)>fY{nDm%sESH+H0dFd|@2xA7xVHQ!bXZI&O!&Mt@GgvbM{ zF=+lr>2^>gwp%q59yl(do?+GeGz5*aDjdQmgXVvQOx<$<ZjF85YxmxT4WVMPsqmfr z@QhggLY@03vq91P8ISwEccBt0zTjyG9$XzN=pQmavnDQfd(1cYH&Q77YmbVn=k7OP zU@X72VWzwPN|a%~Gg#xgEMSx{*q6pm80If*ZQ!D<O<*2^vr_E73b8-ViQN<D9nxp! zKQFucH30Z&w;yv4@QkIM*e+EO{z6BSF{Jz5dykthTk;N`0mN;f1l*Sc@RSqj4f9Uw zml^+~e`Ay&weTRfl55H3tNBBg;%dQifSNxdfd@UyP%Yzxez}SV5Vrh<&PF(MRr5%B znZi>JXmPczqsJ)gb5!u<ZNO!@i?O;1tNPXa>_6>0#!p2)0?+PB(gNCW&^M$Oe(u?g z6BnNKJPglJ;VIAK{Q0bB4}ZSkc@Fz`9>y53&U0LVc}LvFM%YCq6Mp=K?uqDR<56hf z8uDEYe6l}FPePxSpUBY1^vJf0OL5p$^NzcX2RIuun%M5rfDqeQW56-o1m^qhy`K-Z zLpQf;bYPSg3lUe;5b*@`Nu8QU_wVBy-MSI@kuR27#GKXrJki&G>q`*J4Dzop{oVeq z4&Qqr^HjI_)e6--2f1w8_%KvVc$PuxfCll@s!3LGe%J;dom2lAQl@3>2yO?qEgmY= z``s@xOkxzMwDT<Ha;)(*W+K+=(OK{x2J2B6RsO9CIThpv19+mq<=+IC{|iu63$yV& zYvPB5#o~T5KHUBgTzYWVuJ$OfMR@jd7Vg-!yqinVuU0|%Ce{4N8iJWZm%D!wh&A~( zeQ7ec;7|K8eU|$SRJ!j3#!=xiK*2STPKZ^qHlmeAoJC6haJrg9V8rosOq{ToKOBUM z{d;(cuEt!__y`7BMu10G<6<4*ituHd4!`@*hqm#HtIqic7$1Oi1EfK|@dnqD`)8uC z&~5xHfZ&*k)*!HWwPkDRRao}jwd7B&%D)i?cN*`xjemqnsBn$dQ@Wo=-4D1I#6&r@ zI%+(S1GpF+xSaPuORQRBH5D+T03%1}#mz2VKU%Q<j9M5mliRoeKB><32rdfGimDTh ztC)Wi>f+w3d&XHK0w#Y8D*XobUiAv)IfiZZDp|>&s&>kl;QTYM%bkZ?z;+r~jAywX zV10EeIc92&F?`wg%1aXLt~GpwGji8iW(_~^v2{Ox!mnO3A6Nd&^EMqO(G+k*{x_H3 z%yt2#6zB%=jJGRXu?e_Z^B*)DW%DaI7`EMRTcTX63a>N8Zpih<LHOb;tO{>T@FtAg zFMqgMy!3&*k!zcqCI<S>pE-(0g73T!&bLuR?ZaG^`5G8IGhpwYRFQfB(=Tp}opX^| z%r+Lcme1IA(%-jH_21cm+}PpCXYm}a<<+Y8{Io^uUuLX38(FW-SM(kw7EkC}Mp>mq zqETgw64es$+tV9KuGgTlUelE|0B%rLbS)1qS%mi>7R4&7lypXmDa)1zmjVr2DLvJb zN&vF73m{3a^0llU?~CZEj1pYZ)v^3me@N*Hc6NjpEM2mq%fF;cjH$@aq*6L6l!#=r z@u;#O<X^EO7~qqEj?sjsEDVM~5MnS}W!x%(hr4cH)?v}LL>eS5UDg$Zxd0;ksvokw zsf}Pr)nYzYXpxR&w4`2DVgj=!w<ei?qw-@Vlih%`<Yv!;-`Ga@$)s{Rx>D17yvkPL zk&4nV$m{hgu~Y`U0@J3oWHhU!lEUIxT+7T*vYMvoy;?@A_)}UThU=NlWG!(eZ-{Ey zct(p#H!Kb=3EZUgf)BWiMUl)phyW#?%<5Vss-$8{B(px(2j*p!mZ@1(s>Q2xf!vm= zb8vWz7X@_YvbvH@Yd!JUW*I{I=Cr14?A4M=E*np-S6ZwEYC(QSCX>p{S6a}KElNDA zM04pxye9&l5l#&TkhfA(dLtV&C6ZJ$9HQuv#GNQiPoW2tJGD$wOH5$O%3g36$N^nj zZmy{(w{t<NuMfh6(~zD^-kD5oOwLfyV>K*WiC&&RfuWwv(u`SVFf^WFxC0~Eqp^5$ zLnHwqna-p$@l+<RZ&m=vX>36xnS`K?#bI8Eqj-<Tr=I|WEgn>?ZNi|N1*IG-xPk#z z*&}2QF+l8A9H_u52^Fm>?!>7%g(0qR3lz2l6p4rhngF8`nhr5hQGgFz)nh3P=4SbF zMEZc0$*w_#lX}>ibH1{qI}}o2F`^UL%1jE@Bmql36<WgJEQBtsYhPN)Xm{nn2o}Hw zi0X?cVWojt@ma;z3Q~0}gfFuWAePb6i3s@GQk*5?AQX2fOF#t+CQMlBSly<kiQ^kF zA;<D<@sBs)=URaWTs&Q5_#F`c$>N230?RF~ru)ZqxS9v*J6uY!p~KbcceUL+Zr9ij z*WCR0plfKSB?v#_Y~drU9~D00VEN&$hI_}Zb`8}JjLDBH*0bvXdy4m1KUcy1fCc6m z7_->be2Yuj)ljTo<Z8Wl>;hL?e%vD0<2VEA{|6+BvQX&1#HHLjCJ&P>TCiZg(z<9# z_jF~p_d4$^Wma3;b-p&=_m$S=u)-?r4vx>f-W%(gJ)JRcc5|N|SqD!&V?BFiEu$qO zUR~RyGjBAaN0@hAHp{%36gH6V^UfXF^g$EHXuaWB2BL*|A>cJHXfrdbM|$pL-X7R@ zaF$5*|Kg_tBhhrRJQX?ZqIS8poE6;GWLH9uaCwD;l?#g1Y!v}C5t=wpqXjemE&=sv z0vF3GdKtUTK9T6L&xNT6%5)lU7h@mqgR!U=e(k13&fjbkEcF-R!rEa_t?}temb7Oj zQ3#h!`Ro$o;T2^>QTV$E_;CbU^`C^7Ld9+q_9;9IW^9unqW|$#L+UU6e+OVv7eVEZ zX7cwAircx#KQkWvON)K{H=xtzVonc=`8U$RP`8iAw%vC)r2Sj?%h*xj9DJY0Sg_lF z5n#3EZ@d~hpnPbX(AKm_HGYxTz&<Y5=z{rbOR2A^3F=pCg%Rg3Rimb;AGMnqL;J^& zdF6SHVT$-#{w@z`yl{pBcpc0uXO_>u5;#0~kvM*QKt9G97KwL49dYS*i6f_;M<mup zmU@P(GP=|=o^p)nxE)&dJ&uI64)}Tpybth8LDM#|H1fXM=D;@`@S+3$kOTgx1OAKy zzRv+a<beOD1OBE1{*D8VFLbs1eAWT~!U4xWBGi&|r32pLfX{NkZ*jnvI^b&^@VgxF zEe`no4*1U;@FxMsczaHSw9H$6<-kAefWPK||G@!&7jT^SxC<5tip+bM-Xro*Zx74i z%j<~blvz1yIF3oTwn;Wc0xvo%S$Cx!PqC~Xg%=GL+XxA~#$xH5-cy|`+?R^N3l8hk z`m!2io_Z$PlitiAL5=8oCY)dyy_Y2t;dnBxhx;OFtS2EK3hODrP?3zDOoZ_rhQXV3 z7>p1vvJBq#dU~}i%!oO5jCaFJBhQdC>#cD;iBwiYnOyU5WL6ToA+t(j79YkG3tnv) zyh*{U8@>r-wQ%*-0p}BjV$Lr5A@u{zEuBF>R1_5!&LsL_mK}y<PR~~Qcq`1u$6|?G zmYZ&=mYfwKUgK#xw;bOF!t!kZTn(?nmTA1hQjewin;wg%gub}$;<)E`C+<i1s>F-D z_AxB!?Sgq87QAcm&X)WZ1fd{K<;Z(q%4tUu6#QQru-N7BkJkdHazdQV_{op@q(%F$ z#nUe5HiA<*c<y77`aI5Cj3va=E+<QHD(4=7OF1V+`zM4?{WCytD(7K>UkY{U&tDLn z@_#9CIWL~0Se~~Yqx{MLx&nP@7nO5N@KFxU`Ck{f-4DMbIF<7rk%Q~P|7HeHyeF{O z<(#$PQToUPN!qnT*wrAOb~zUloa*x}flE0<qMagqd=-&?#_!!&P(H=Ki2xLQU<xkz zeFB$$lljb5B45I8+&hvV5jgU{3IAk?5q>j(lAk2_6$IbqAm<Un$5dC!IY{`pzL@u6 zxmYA#s3-1a$)754X_t6iV_R*w-2VoVgYVE%&Lae;>+%?p!_O1p$32996_K--@KJvS z{$V*l_;lX!;*dhI-+_NppUDE3c6|?pK$$}LnBq%*E5WJ$Z8kXpE`_l<girOKPxy4* zZzg<&%)5f{(cU#e{wl(!_V(HMF~QF{@LwSK6f$p#;52S85WI!(>rgo;(w`}-$;JsB z{eKPolm5Jf;MAYrCUS79b`cL#2%nyxvj|S*_-%61Le4_Mr*b+8AFJ|wUuDDPepyT8 zP`m!d#y3C~mJH$3_4-?adw?hX^OQ}F+;_hud^+zw8(;3nA;PEgo+LQ++i4;P{r|A= z+XsYC{Z=M?tjhRo5DA2R-6siL#>xFK2Fny19<yLJ%|T8(!Ra}y5_~Gikp6s$;PgEI z4Z)`o{sn<cJs%hPd`0-w5B1`}M|;Il#79gf__csYeXbC=l)p#FpGNrf+?YXdlrQD1 zBzP;q!vdG`pA+)qgiqyfAvnqxP4-h8E^XT*a4G-ag#2F-K9zsK#y>3hzjffhMR2-~ zF7fJ*ex`ZRWP(qJKIxwy2wduURLt8>_}?Y`PJ&bU-2}(GLdyTOP5!Gw{z1a0aq<Si z>Aw4;P0n#4=QQC{Ig`*JDAKOCtR`y~IQob3XA*n{^hy0^+2ouSa&9Jkx{kNn_@4;= zD#EApt|vIPcY{riOXOQWCVVPq7s08V2W)cWcdNfAe5${8v86Ejhn^e#1gCk|!vv>! z*FO<l?g^B9fZ$$eOFzF(aQsFl@sk2abx|kzo$VCiw-Nq0@c|9_c;1R_!e$E`WuQLr zf7|i0(8ibd*WH9q^Sz%D9OXzopSH=7_t9H6T!ArI-X(JAy>qEh0QIE#*mntz^Gct7 zU*OW-H7Fd)4TMkSt2Vy8M>-w&n+Z<yp9cw!x=24iW|Kch#O;%W?*qQ{+kS%6xP8SY zCxAvld5!RC+`d6@1$a{aI|Qfl&)VcK5qf?~_;i2Oxo~zX23z1M<zFOl>3^Bmxd@-; zA(s)H>f<4DFt3pL*mT0D`phMKjPtP2zn$=Dey-a1!c5jl_*Bns8(-#2KO%e@SDS5o z8UNb|pUThM`1rjLOVNhQc>X&FIX@>j_2&~r4*GKlHx!gTgiqsVknqu;a$g)Ee5(Ip z8(-#WXKZ-Cu<I<5L;dD)bG0h>)O~_KUEruT<*%~w@w+LOwGRBdZTyD>f4c+!F&qCe z!GF?$zt6_U@3L6_-GP71#@{3OuRHL6Z{y22eA|KlzXYf8@EMVV@h~jn;cLRD@o<gM z3u$!UH4{Gm?MI#iD+oT5;0n~Spg1ulZ@2T`DlY$SvcrbU`)-d7m*2fl*>L$?`u_oq Cg}__@ literal 0 HcmV?d00001 diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c new file mode 100644 index 0000000..7802d52 --- /dev/null +++ b/tc/tc_qdisc.c @@ -0,0 +1,319 @@ +/* + * tc_qdisc.c "tc 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. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * J Hadi Salim: Extension to ingress + */ + +#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 <math.h> + +#include "utils.h" +#include "tc_util.h" +#include "tc_common.h" + +static int usage(void); + +static int usage(void) +{ + fprintf(stderr, "Usage: tc qdisc [ add | del | replace | change | get ] dev STRING\n"); + fprintf(stderr, " [ handle QHANDLE ] [ root | ingress | parent CLASSID ]\n"); + fprintf(stderr, " [ estimator INTERVAL TIME_CONSTANT ]\n"); + fprintf(stderr, " [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " tc qdisc show [ dev STRING ] [ingress]\n"); + fprintf(stderr, "Where:\n"); + fprintf(stderr, "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"); + fprintf(stderr, "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n"); + return -1; +} + +int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv) +{ + struct qdisc_util *q = NULL; + struct tc_estimator est; + char d[16]; + char k[16]; + struct { + struct nlmsghdr n; + struct tcmsg t; + char buf[TCA_BUF_MAX]; + } req; + + memset(&req, 0, sizeof(req)); + memset(&est, 0, sizeof(est)); + memset(&d, 0, sizeof(d)); + memset(&k, 0, sizeof(k)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + req.t.tcm_family = AF_UNSPEC; + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + if (d[0]) + duparg("dev", *argv); + strncpy(d, *argv, sizeof(d)-1); + } else if (strcmp(*argv, "handle") == 0) { + __u32 handle; + if (req.t.tcm_handle) + duparg("handle", *argv); + NEXT_ARG(); + if (get_qdisc_handle(&handle, *argv)) + invarg(*argv, "invalid qdisc ID"); + req.t.tcm_handle = handle; + } else if (strcmp(*argv, "root") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"root\" is duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_ROOT; +#ifdef TC_H_INGRESS + } else if (strcmp(*argv, "ingress") == 0) { + if (req.t.tcm_parent) { + fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n"); + return -1; + } + req.t.tcm_parent = TC_H_INGRESS; + strncpy(k, "ingress", sizeof(k)-1); + q = get_qdisc_kind(k); + req.t.tcm_handle = 0xffff0000; + + argc--; argv++; + break; +#endif + } else if (strcmp(*argv, "parent") == 0) { + __u32 handle; + NEXT_ARG(); + if (req.t.tcm_parent) + duparg("parent", *argv); + if (get_tc_classid(&handle, *argv)) + invarg(*argv, "invalid parent ID"); + req.t.tcm_parent = handle; + } else if (matches(*argv, "estimator") == 0) { + if (parse_estimator(&argc, &argv, &est)) + return -1; + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + strncpy(k, *argv, sizeof(k)-1); + + q = get_qdisc_kind(k); + argc--; argv++; + break; + } + argc--; argv++; + } + + if (k[0]) + addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1); + if (est.ewma_log) + addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est)); + + if (q) { + if (q->parse_qopt(q, argc, argv, &req.n)) + return 1; + } else { + if (argc) { + if (matches(*argv, "help") == 0) + usage(); + + fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv); + return -1; + } + } + + if (d[0]) { + int idx; + + ll_init_map(&rth); + + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + req.t.tcm_ifindex = idx; + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + return 2; + + return 0; +} + +static int filter_ifindex; + +static int print_qdisc(const struct sockaddr_nl *who, + struct nlmsghdr *n, + void *arg) +{ + FILE *fp = (FILE*)arg; + struct tcmsg *t = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr * tb[TCA_MAX+1]; + struct qdisc_util *q; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) { + fprintf(stderr, "Not a qdisc\n"); + return 0; + } + len -= NLMSG_LENGTH(sizeof(*t)); + if (len < 0) { + fprintf(stderr, "Wrong len %d\n", len); + return -1; + } + + if (filter_ifindex && filter_ifindex != t->tcm_ifindex) + return 0; + + memset(tb, 0, sizeof(tb)); + parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len); + + if (tb[TCA_KIND] == NULL) { + fprintf(stderr, "print_qdisc: NULL kind\n"); + return -1; + } + + if (n->nlmsg_type == RTM_DELQDISC) + fprintf(fp, "deleted "); + + fprintf(fp, "qdisc %s %x: ", (char*)RTA_DATA(tb[TCA_KIND]), t->tcm_handle>>16); + if (filter_ifindex == 0) + fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex)); + if (t->tcm_parent == TC_H_ROOT) + fprintf(fp, "root "); + else if (t->tcm_parent) { + print_tc_classid(abuf, sizeof(abuf), t->tcm_parent); + fprintf(fp, "parent %s ", abuf); + } + if (t->tcm_info != 1) { + fprintf(fp, "refcnt %d ", t->tcm_info); + } + /* pfifo_fast is generic enough to warrant the hardcoding --JHS */ + + if (0 == strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND]))) + q = get_qdisc_kind("prio"); + else + q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND])); + + if (tb[TCA_OPTIONS]) { + if (q) + q->print_qopt(q, fp, tb[TCA_OPTIONS]); + else + fprintf(fp, "[cannot parse qdisc parameters]"); + } + fprintf(fp, "\n"); + if (show_stats) { + struct rtattr *xstats = NULL; + + if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) { + print_tcstats_attr(fp, tb, " ", &xstats); + fprintf(fp, "\n"); + } + + if (q && xstats && q->print_xstats) { + q->print_xstats(q, fp, xstats); + fprintf(fp, "\n"); + } + } + fflush(fp); + return 0; +} + + +int tc_qdisc_list(int argc, char **argv) +{ + struct tcmsg t; + char d[16]; + + memset(&t, 0, sizeof(t)); + t.tcm_family = AF_UNSPEC; + memset(&d, 0, sizeof(d)); + + while (argc > 0) { + if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(d, *argv, sizeof(d)-1); +#ifdef TC_H_INGRESS + } else if (strcmp(*argv, "ingress") == 0) { + if (t.tcm_parent) { + fprintf(stderr, "Duplicate parent ID\n"); + usage(); + } + t.tcm_parent = TC_H_INGRESS; +#endif + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv); + return -1; + } + + argc--; argv++; + } + + ll_init_map(&rth); + + if (d[0]) { + if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return 1; + } + filter_ifindex = t.tcm_ifindex; + } + + if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { + perror("Cannot send dump request"); + return 1; + } + + if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + return 0; +} + +int do_qdisc(int argc, char **argv) +{ + if (argc < 1) + return tc_qdisc_list(0, NULL); + if (matches(*argv, "add") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "change") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1); + if (matches(*argv, "replace") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "link") == 0) + return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return tc_qdisc_modify(RTM_DELQDISC, 0, argc-1, argv+1); +#if 0 + if (matches(*argv, "get") == 0) + return tc_qdisc_get(RTM_GETQDISC, 0, argc-1, argv+1); +#endif + if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 + || matches(*argv, "lst") == 0) + return tc_qdisc_list(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv); + return -1; +} diff --git a/tc/tc_qdisc.o b/tc/tc_qdisc.o new file mode 100644 index 0000000000000000000000000000000000000000..6b619ec19facb24f22a583cc8596a58da0617744 GIT binary patch literal 11424 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 K;PQuZVE+di6PTa? literal 0 HcmV?d00001 diff --git a/tc/tc_red.c b/tc/tc_red.c new file mode 100644 index 0000000..385e7af --- /dev/null +++ b/tc/tc_red.c @@ -0,0 +1,97 @@ +/* + * tc_red.c RED maintanance routines. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <math.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "tc_core.h" +#include "tc_red.h" + +/* + Plog = log(prob/(qmax - qmin)) + */ +int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob) +{ + int i = qmax - qmin; + + if (i <= 0) + return -1; + + prob /= i; + + for (i=0; i<32; i++) { + if (prob > 1.0) + break; + prob *= 2; + } + if (i>=32) + return -1; + return i; +} + +/* + burst + 1 - qmin/avpkt < (1-(1-W)^burst)/W + */ + +int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt) +{ + int wlog = 1; + double W = 0.5; + double a = (double)burst + 1 - (double)qmin/avpkt; + + if (a < 1.0) + return -1; + for (wlog=1; wlog<32; wlog++, W /= 2) { + if (a <= (1 - pow(1-W, burst))/W) + return wlog; + } + return -1; +} + +/* + Stab[t>>Scell_log] = -log(1-W) * t/xmit_time + */ + +int tc_red_eval_idle_damping(int Wlog, unsigned avpkt, unsigned bps, __u8 *sbuf) +{ + double xmit_time = tc_core_usec2tick(1000000*(double)avpkt/bps); + double lW = -log(1.0 - 1.0/(1<<Wlog))/xmit_time; + double maxtime = 31/lW; + int clog; + int i; + double tmp; + + tmp = maxtime; + for (clog=0; clog<32; clog++) { + if (maxtime/(1<<clog) < 512) + break; + } + if (clog >= 32) + return -1; + + sbuf[0] = 0; + for (i=1; i<255; i++) { + sbuf[i] = (i<<clog)*lW; + if (sbuf[i] > 31) + sbuf[i] = 31; + } + sbuf[255] = 31; + return clog; +} diff --git a/tc/tc_red.h b/tc/tc_red.h new file mode 100644 index 0000000..6f6b09e --- /dev/null +++ b/tc/tc_red.h @@ -0,0 +1,8 @@ +#ifndef _TC_RED_H_ +#define _TC_RED_H_ 1 + +extern int tc_red_eval_P(unsigned qmin, unsigned qmax, double prob); +extern int tc_red_eval_ewma(unsigned qmin, unsigned burst, unsigned avpkt); +extern int tc_red_eval_idle_damping(int wlog, unsigned avpkt, unsigned bandwidth, __u8 *sbuf); + +#endif diff --git a/tc/tc_red.o b/tc/tc_red.o new file mode 100644 index 0000000000000000000000000000000000000000..866ee10269dd975d26b5c9e61a5d5ff9d137c4c1 GIT binary patch literal 2976 zcmbVOUrbw77(WFjbzpBHQ}JJVAF6=~H!UDGnrxkwtxJ%glL)TbUE14?K<Uz6VS5=R z-OTPA@ySP@^wq={qRBGBbj)N4+l-ls54gm{1QW4FvN=QjzH`3A4!0#H`_i88`+ncw z^Yz?wJ3lydw87(Hl057Xd#Fj2u{(_q<tP`U%*R?-*LFqouP#-AE^S@tu4o6#;jN1H z;c7-}--mu1E!A>G8@pcG4$NyA%~+lN14L(YzTvMG9=4LnWEOWmPSv?JT8V21uQ`7s z`~C#3zjDub>G#Mt*c{}35_1E80XfwB7HZtBXo+<w@x!dXF+o|YD-~^=JCz;;=1<5- zOnL)4U#@6v?*?@U1i!XK&<|@c_!}h$3j}a9u?A8w2$gPQh5Q%Jop|ZH$jJ{@kn#3F z&MZ1x)PFhd+z=aZ9zf$>XoJs#_Zru_@9`q&an<>033qjS9j3Pm4KxLz7CHOZAmAqS zw=Dc&*m4iyDoA{5UyGp}xSrA8dx8eiioM?4Xs%u>RRi-GXU(|}`@PGHTm=&j>gU;C z(0IH&$k-L0W6{}k?hmEP!fh$QhdU0#?OrSXqib(0SIZ0A+khUq+|+sDR{2sD<N%Do z>)aEw@XA?WACfi?3u&FW(2Ez1BLbK9!%?9Zi3<(ZqLdDh;9;I~{?=yiBDM{L`14#6 zON`_o@O}lqLh_)`|8c`xzK&tvNC}TN`Xv;<{gjvc4f{GujYoYt+K<Izuj!q~hDW>f zKBM30)q6vs{%|P#s^0mImDb~?E%2TJBa`auV$3L<&)Vh$a62#f87bwhY16Q+b2c;5 zrfo7~qEKK)K8K2tD%cTK2@imV0oIvhCU0gfX3XSl%YasT3bvV=Vn!;L%~~_IvW-U; zb%?|fJm$&}CH>ik-3I?|l|2V~lM6#t-!H%j&9@a)6ukmw9mHQET)B3*CE=<|?lrZ# z!GJj*H9icn*jGDzRXRoWBPs~8>|gRjfYpotjN;w%lDgz?Q@ne-UlLFRyGpXI#D+kq zH~+B~;SncCoPRTXWxSmKNx<kQB2U3W?tzlk?K|~9@i<?V-0^K-tXIk;(0$0=@u<6J zhxNvAvp`WY;&q9?v3viI16Hs8r)U7WEZENKuD@e&3Fz0v{={puh8pWm9Q&x(3F7sB zVZ`~%qfUPJ(=1#X`3b0Thdi`d#Uviz+WZ>#*~jGhl_7X9Gw4UoSK`kQF3+391BB}m zApRoZoi6-k!b2`xoUgt1OP7!mGXQeCGXEIa-Xwg4bou@m1>L<@bQy;)=JC^kknfTF z9{v~iz?q#&=B>1mV#ZJ`jLR#y^mCQ#;a)EH@s;Bd3QAhD=5%somuAgnO?Eanw+oz1 zPg}{fnLRr>a~ch)T;584RIpOL_9VPdrgNtm|9=5J;^Ts+;a~ix2Xr1}B*bx=5???N z2ywh>B>p*qKyLgi0oUaDQNi&H$vizYSImQVm&9LJ@TU~~eFev7UHVTc_|pnLr{F#X lUr=y7>oU(L3XbPN;yTDkXvSV8$@hlD<+(aV4icB==Rb!i(#rq< literal 0 HcmV?d00001 diff --git a/tc/tc_util.c b/tc/tc_util.c new file mode 100644 index 0000000..9cde144 --- /dev/null +++ b/tc/tc_util.c @@ -0,0 +1,519 @@ +/* + * tc_util.c Misc TC utility functions. + * + * 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: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#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 <math.h> + +#include "utils.h" +#include "tc_util.h" + +int get_qdisc_handle(__u32 *h, const char *str) +{ + __u32 maj; + char *p; + + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) + return -1; + maj <<= 16; + if (*p != ':' && *p!=0) + return -1; +ok: + *h = maj; + return 0; +} + +int get_tc_classid(__u32 *h, const char *str) +{ + __u32 maj, min; + char *p; + + maj = TC_H_ROOT; + if (strcmp(str, "root") == 0) + goto ok; + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) { + maj = 0; + if (*p != ':') + return -1; + } + if (*p == ':') { + if (maj >= (1<<16)) + return -1; + maj <<= 16; + str = p+1; + min = strtoul(str, &p, 16); + if (*p != 0) + return -1; + if (min >= (1<<16)) + return -1; + maj |= min; + } else if (*p != 0) + return -1; + +ok: + *h = maj; + return 0; +} + +int print_tc_classid(char *buf, int len, __u32 h) +{ + if (h == TC_H_ROOT) + sprintf(buf, "root"); + else if (h == TC_H_UNSPEC) + snprintf(buf, len, "none"); + else if (TC_H_MAJ(h) == 0) + snprintf(buf, len, ":%x", TC_H_MIN(h)); + else if (TC_H_MIN(h) == 0) + snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); + else + snprintf(buf, len, "%x:%x", TC_H_MAJ(h)>>16, TC_H_MIN(h)); + return 0; +} + +char * sprint_tc_classid(__u32 h, char *buf) +{ + if (print_tc_classid(buf, SPRINT_BSIZE-1, h)) + strcpy(buf, "???"); + return buf; +} + +/* See http://physics.nist.gov/cuu/Units/binary.html */ +static const struct rate_suffix { + const char *name; + double scale; +} suffixes[] = { + { "bit", 1. }, + { "Kibit", 1024. }, + { "kbit", 1000. }, + { "mibit", 1024.*1024. }, + { "mbit", 1000000. }, + { "gibit", 1024.*1024.*1024. }, + { "gbit", 1000000000. }, + { "tibit", 1024.*1024.*1024.*1024. }, + { "tbit", 1000000000000. }, + { "Bps", 8. }, + { "KiBps", 8.*1024. }, + { "KBps", 8000. }, + { "MiBps", 8.*1024*1024. }, + { "MBps", 8000000. }, + { "GiBps", 8.*1024.*1024.*1024. }, + { "GBps", 8000000000. }, + { "TiBps", 8.*1024.*1024.*1024.*1024. }, + { "TBps", 8000000000000. }, + { NULL } +}; + + +int get_rate(unsigned *rate, const char *str) +{ + char *p; + double bps = strtod(str, &p); + const struct rate_suffix *s; + + if (p == str) + return -1; + + if (*p == '\0') { + *rate = bps / 8.; /* assume bytes/sec */ + return 0; + } + + for (s = suffixes; s->name; ++s) { + if (strcasecmp(s->name, p) == 0) { + *rate = (bps * s->scale) / 8.; + return 0; + } + } + + return -1; +} + +int get_rate_and_cell(unsigned *rate, int *cell_log, char *str) +{ + char * slash = strchr(str, '/'); + + if (slash) + *slash = 0; + + if (get_rate(rate, str)) + return -1; + + if (slash) { + int cell; + int i; + + if (get_integer(&cell, slash+1, 0)) + return -1; + *slash = '/'; + + for (i=0; i<32; i++) { + if ((1<<i) == cell) { + *cell_log = i; + return 0; + } + } + return -1; + } + return 0; +} + +void print_rate(char *buf, int len, __u32 rate) +{ + double tmp = (double)rate*8; + extern int use_iec; + + if (use_iec) { + 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) + snprintf(buf, len, "%.0fMbit", tmp/1000000.0); + else if (tmp >= 1000.0 * 1000.0) + snprintf(buf, len, "%.0fKbit", tmp/1000.0); + else + snprintf(buf, len, "%.0fbit", tmp); + } +} + +char * sprint_rate(__u32 rate, char *buf) +{ + print_rate(buf, SPRINT_BSIZE-1, rate); + return buf; +} + +int get_usecs(unsigned *usecs, const char *str) +{ + double t; + char *p; + + t = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || + strcasecmp(p, "secs")==0) + t *= 1000000; + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || + strcasecmp(p, "msecs") == 0) + t *= 1000; + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || + strcasecmp(p, "usecs") == 0) + t *= 1; + else + return -1; + } + + *usecs = t; + return 0; +} + + +void print_usecs(char *buf, int len, __u32 usec) +{ + double tmp = usec; + + if (tmp >= 1000000) + snprintf(buf, len, "%.1fs", tmp/1000000); + else if (tmp >= 1000) + snprintf(buf, len, "%.1fms", tmp/1000); + else + snprintf(buf, len, "%uus", usec); +} + +char * sprint_usecs(__u32 usecs, char *buf) +{ + print_usecs(buf, SPRINT_BSIZE-1, usecs); + return buf; +} + +int get_size(unsigned *size, const char *str) +{ + double sz; + char *p; + + sz = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k")==0) + sz *= 1024; + else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g")==0) + sz *= 1024*1024*1024; + else if (strcasecmp(p, "gbit") == 0) + sz *= 1024*1024*1024/8; + else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m")==0) + sz *= 1024*1024; + else if (strcasecmp(p, "mbit") == 0) + sz *= 1024*1024/8; + else if (strcasecmp(p, "kbit") == 0) + sz *= 1024/8; + else if (strcasecmp(p, "b") != 0) + return -1; + } + + *size = sz; + return 0; +} + +int get_size_and_cell(unsigned *size, int *cell_log, char *str) +{ + char * slash = strchr(str, '/'); + + if (slash) + *slash = 0; + + if (get_size(size, str)) + return -1; + + if (slash) { + int cell; + int i; + + if (get_integer(&cell, slash+1, 0)) + return -1; + *slash = '/'; + + for (i=0; i<32; i++) { + if ((1<<i) == cell) { + *cell_log = i; + return 0; + } + } + return -1; + } + return 0; +} + +void print_size(char *buf, int len, __u32 sz) +{ + double tmp = sz; + + if (sz >= 1024*1024 && fabs(1024*1024*rint(tmp/(1024*1024)) - sz) < 1024) + snprintf(buf, len, "%gMb", rint(tmp/(1024*1024))); + else if (sz >= 1024 && fabs(1024*rint(tmp/1024) - sz) < 16) + snprintf(buf, len, "%gKb", rint(tmp/1024)); + else + snprintf(buf, len, "%ub", sz); +} + +char * sprint_size(__u32 size, char *buf) +{ + print_size(buf, SPRINT_BSIZE-1, size); + return buf; +} + +static const double max_percent_value = 0xffffffff; + +int get_percent(__u32 *percent, const char *str) +{ + char *p; + double per = strtod(str, &p) / 100.; + + if (per > 1. || per < 0) + return -1; + if (*p && strcmp(p, "%")) + return -1; + + *percent = (unsigned) rint(per * max_percent_value); + return 0; +} + +void print_percent(char *buf, int len, __u32 per) +{ + snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value); +} + +char * sprint_percent(__u32 per, char *buf) +{ + print_percent(buf, SPRINT_BSIZE-1, per); + return buf; +} + +void print_qdisc_handle(char *buf, int len, __u32 h) +{ + snprintf(buf, len, "%x:", TC_H_MAJ(h)>>16); +} + +char * sprint_qdisc_handle(__u32 h, char *buf) +{ + print_qdisc_handle(buf, SPRINT_BSIZE-1, h); + return buf; +} + +char * action_n2a(int action, char *buf, int len) +{ + switch (action) { + case -1: + return "continue"; + break; + case TC_ACT_OK: + return "pass"; + break; + case TC_ACT_SHOT: + return "drop"; + break; + case TC_ACT_RECLASSIFY: + return "reclassify"; + case TC_ACT_PIPE: + return "pipe"; + case TC_ACT_STOLEN: + return "stolen"; + default: + snprintf(buf, len, "%d", action); + return buf; + } +} + +int action_a2n(char *arg, int *result) +{ + int res; + + if (matches(arg, "continue") == 0) + res = -1; + else if (matches(arg, "drop") == 0) + res = TC_ACT_SHOT; + else if (matches(arg, "shot") == 0) + res = TC_ACT_SHOT; + else if (matches(arg, "pass") == 0) + res = TC_ACT_OK; + else if (strcmp(arg, "ok") == 0) + res = TC_ACT_OK; + else if (matches(arg, "reclassify") == 0) + res = TC_ACT_RECLASSIFY; + else { + char dummy; + if (sscanf(arg, "%d%c", &res, &dummy) != 1) + return -1; + } + *result = res; + return 0; +} + +void print_tm(FILE * f, const struct tcf_t *tm) +{ + int hz = get_user_hz(); + if (tm->install != 0) + fprintf(f, " installed %u sec", (unsigned)(tm->install/hz)); + if (tm->lastuse != 0) + fprintf(f, " used %u sec", (unsigned)(tm->lastuse/hz)); + if (tm->expires != 0) + fprintf(f, " expires %u sec", (unsigned)(tm->expires/hz)); +} + +void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats) +{ + SPRINT_BUF(b1); + struct rtattr *tbs[TCA_STATS_MAX + 1]; + + parse_rtattr_nested(tbs, TCA_STATS_MAX, rta); + + if (tbs[TCA_STATS_BASIC]) { + struct gnet_stats_basic bs = {0}; + memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs))); + fprintf(fp, "%sSent %llu bytes %u pkt", + prefix, (unsigned long long) bs.bytes, bs.packets); + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); + fprintf(fp, " (dropped %u, overlimits %u requeues %u) ", + q.drops, q.overlimits, q.requeues); + } + + 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))); + fprintf(fp, "\n%srate %s %upps ", + prefix, sprint_rate(re.bps, b1), re.pps); + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue q = {0}; + memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]), MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q))); + if (!tbs[TCA_STATS_RATE_EST]) + fprintf(fp, "\n%s", prefix); + fprintf(fp, "backlog %s %up requeues %u ", + sprint_size(q.backlog, b1), q.qlen, q.requeues); + } + + if (xstats) + *xstats = tbs[TCA_STATS_APP] ? : NULL; +} + +void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats) +{ + SPRINT_BUF(b1); + + if (tb[TCA_STATS2]) { + print_tcstats2_attr(fp, tb[TCA_STATS2], prefix, xstats); + if (xstats && NULL == *xstats) + goto compat_xstats; + return; + } + /* backward compatibility */ + if (tb[TCA_STATS]) { + struct tc_stats st; + + /* handle case where kernel returns more/less than we know about */ + memset(&st, 0, sizeof(st)); + memcpy(&st, RTA_DATA(tb[TCA_STATS]), MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st))); + + fprintf(fp, "%sSent %llu bytes %u pkts (dropped %u, overlimits %u) ", + prefix, (unsigned long long)st.bytes, st.packets, st.drops, + st.overlimits); + + if (st.bps || st.pps || st.qlen || st.backlog) { + fprintf(fp, "\n%s", prefix); + if (st.bps || st.pps) { + fprintf(fp, "rate "); + if (st.bps) + fprintf(fp, "%s ", sprint_rate(st.bps, b1)); + if (st.pps) + fprintf(fp, "%upps ", st.pps); + } + if (st.qlen || st.backlog) { + fprintf(fp, "backlog "); + if (st.backlog) + fprintf(fp, "%s ", sprint_size(st.backlog, b1)); + if (st.qlen) + fprintf(fp, "%up ", st.qlen); + } + } + } + +compat_xstats: + if (tb[TCA_XSTATS] && xstats) + *xstats = tb[TCA_XSTATS]; +} + diff --git a/tc/tc_util.h b/tc/tc_util.h new file mode 100644 index 0000000..1aa1bda --- /dev/null +++ b/tc/tc_util.h @@ -0,0 +1,84 @@ +#ifndef _TC_UTIL_H_ +#define _TC_UTIL_H_ 1 + +#define MAX_MSG 16384 +#include <linux/pkt_sched.h> +#include <linux/pkt_cls.h> +#include <linux/gen_stats.h> +#include "tc_core.h" + +struct qdisc_util +{ + struct qdisc_util *next; + const char *id; + int (*parse_qopt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); + int (*print_qopt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); + int (*print_xstats)(struct qdisc_util *qu, FILE *f, struct rtattr *xstats); + + int (*parse_copt)(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n); + int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); +}; + +struct filter_util +{ + struct filter_util *next; + char id[16]; + int (*parse_fopt)(struct filter_util *qu, char *fhandle, int argc, + char **argv, struct nlmsghdr *n); + int (*print_fopt)(struct filter_util *qu, FILE *f, struct rtattr *opt, __u32 fhandle); +}; + +struct action_util +{ + struct action_util *next; + char id[16]; + int (*parse_aopt)(struct action_util *a, int *argc, char ***argv, + int code, struct nlmsghdr *n); + int (*print_aopt)(struct action_util *au, FILE *f, struct rtattr *opt); + int (*print_xstats)(struct action_util *au, FILE *f, struct rtattr *xstats); +}; + +extern struct qdisc_util *get_qdisc_kind(const char *str); +extern struct filter_util *get_filter_kind(const char *str); + +extern int get_qdisc_handle(__u32 *h, const char *str); +extern int get_rate(unsigned *rate, const char *str); +extern int get_percent(unsigned *percent, const char *str); +extern int get_size(unsigned *size, const char *str); +extern int get_size_and_cell(unsigned *size, int *cell_log, char *str); +extern int get_usecs(unsigned *usecs, const char *str); +extern void print_rate(char *buf, int len, __u32 rate); +extern void print_size(char *buf, int len, __u32 size); +extern void print_percent(char *buf, int len, __u32 percent); +extern void print_qdisc_handle(char *buf, int len, __u32 h); +extern void print_usecs(char *buf, int len, __u32 usecs); +extern char * sprint_rate(__u32 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); +extern char * sprint_usecs(__u32 usecs, char *buf); +extern char * sprint_percent(__u32 percent, char *buf); + +extern void print_tcstats_attr(FILE *fp, struct rtattr *tb[], char *prefix, struct rtattr **xstats); +extern void print_tcstats2_attr(FILE *fp, struct rtattr *rta, char *prefix, struct rtattr **xstats); + +extern int get_tc_classid(__u32 *h, const char *str); +extern int print_tc_classid(char *buf, int len, __u32 h); +extern char * sprint_tc_classid(__u32 h, char *buf); + +extern int tc_print_police(FILE *f, struct rtattr *tb); +extern int parse_police(int *, char ***, int, struct nlmsghdr *); + +extern char *action_n2a(int action, char *buf, int len); +extern int action_a2n(char *arg, int *result); +extern int act_parse_police(struct action_util *a,int *, char ***, int, struct nlmsghdr *); +extern int print_police(struct action_util *a, FILE *f, + struct rtattr *tb); +extern int police_print_xstats(struct action_util *a,FILE *f, + struct rtattr *tb); +extern int tc_print_action(FILE *f, const struct rtattr *tb); +extern int tc_print_ipt(FILE *f, const struct rtattr *tb); +extern int parse_action(int *, char ***, int, struct nlmsghdr *); +extern void print_tm(FILE *f, const struct tcf_t *tm); + +#endif diff --git a/tc/tc_util.o b/tc/tc_util.o new file mode 100644 index 0000000000000000000000000000000000000000..bfe5a316ab10c362635e97c1e72c01fd2cd07cd9 GIT binary patch literal 16896 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 Yz18;Xznc{sxc+;W9v{tLOAE*U0y|_)2mk;8 literal 0 HcmV?d00001 diff --git a/testsuite/Makefile b/testsuite/Makefile new file mode 100644 index 0000000..5661cea --- /dev/null +++ b/testsuite/Makefile @@ -0,0 +1,33 @@ +TESTS := $(patsubst tests/%,%,$(wildcard tests/*)) +IPVERS := $(filter-out iproute2/Makefile,$(wildcard iproute2/*)) + +DEV := eth0 + +.PHONY: compile listtests alltests $(TESTS) + +compile: + echo "Entering iproute2" && cd iproute2 && $(MAKE) && cd ..; + +listtests: + @for t in $(TESTS); do \ + echo "$$t"; \ + done + +alltests: $(TESTS) + +clean: + @rm -rf results/* + +$(TESTS): + @for i in $(IPVERS); do \ + echo -n "Running $@ with $$i on `uname -r`: "; \ + logger "TESTMARK: $@"; \ + o=`echo $$i | sed -e 's/iproute2\///'`; \ + TC="$$i/tc/tc" IP="$$i/ip/ip" DEV="$(DEV)" sudo tests/$@ > results/$@.$$o.out 2> results/$@.$$o.err; \ + dmesg > results/$@.$$o.dmesg; \ + if [ -z "`cat results/$@.$$o.err`" ]; then \ + echo "PASS"; \ + else \ + echo "FAILED"; \ + fi \ + done diff --git a/testsuite/configs/all-2.4 b/testsuite/configs/all-2.4 new file mode 100644 index 0000000..cc4131c --- /dev/null +++ b/testsuite/configs/all-2.4 @@ -0,0 +1,848 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_X86=y +# CONFIG_SBUS is not set +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMIII is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MELAN is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_X86_HAS_TSC=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_PGE=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_X86_F00F_WORKS_OK=y +CONFIG_X86_MCE=y +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_HIGHMEM is not set +# CONFIG_MATH_EMULATION is not set +# CONFIG_MTRR is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=32 +# CONFIG_X86_NUMA is not set +# CONFIG_X86_TSC_DISABLE is not set +CONFIG_X86_TSC=y +CONFIG_HAVE_DEC_LOCK=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_ISA=y +CONFIG_PCI_NAMES=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +CONFIG_HOTPLUG=y + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +CONFIG_CARDBUS=y +# CONFIG_TCIC is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HOTPLUG_PCI_COMPAQ is not set +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +# CONFIG_HOTPLUG_PCI_IBM is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set +# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +# CONFIG_OOM_KILLER is not set +CONFIG_PM=y +# CONFIG_APM is not set + +# +# ACPI Support +# +# CONFIG_ACPI is not set +CONFIG_ACPI_BOOT=y + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +CONFIG_PNP=y +CONFIG_ISAPNP=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_CISS_MONITOR_THREAD is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_STATS is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_NAT=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_TOS=y +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +CONFIG_ATM_CLIP=y +# CONFIG_ATM_CLIP_NO_ICMP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_CSZ=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set +# CONFIG_PHONE_IXJ_PCMCIA is not set + +# +# ATA/IDE/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +# CONFIG_IDEDISK_STROKE is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_DELKIN is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_BLK_DEV_GENERIC is not set +CONFIG_IDEPCI_SHARE_IRQ=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_PCI_WIP is not set +# CONFIG_BLK_DEV_ADMA100 is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_WDC_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_HPT34X_AUTODMA is not set +# CONFIG_BLK_DEV_HPT366 is not set +CONFIG_BLK_DEV_PIIX=y +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_PDC202XX_BURST is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +CONFIG_BLK_DEV_RZ1000=y +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_IDEDMA_IVB is not set +# CONFIG_DMA_NONPCI is not set +# CONFIG_BLK_DEV_ATARAID is not set +# CONFIG_BLK_DEV_ATARAID_PDC is not set +# CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set +# CONFIG_BLK_DEV_ATARAID_SII is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set +# CONFIG_FUSION_BOOT is not set +# CONFIG_FUSION_ISENSE is not set +# CONFIG_FUSION_CTL is not set +# CONFIG_FUSION_LAN is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_PCI is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_CS89x0 is not set +# CONFIG_TULIP is not set +# CONFIG_DE4X5 is not set +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_EEPRO100_PIO is not set +# CONFIG_E100 is not set +# CONFIG_LNE390 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNDANCE_MMIO is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_RHINE_MMIO is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_UINPUT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +CONFIG_MOUSE=y +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_MK712_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMI_PANIC_EVENT is not set +# CONFIG_IPMI_DEVICE_INTERFACE is not set +# CONFIG_IPMI_KCS is not set +# CONFIG_IPMI_WATCHDOG is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_AMD_RNG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_AMD_PM768 is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set + +# +# Direct Rendering Manager (XFree86 DRI support) +# +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_PCMCIA_SERIAL_CS is not set +# CONFIG_SYNCLINK_CS is not set +# CONFIG_MWAVE is not set +# CONFIG_OBMOUSE is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_QFMT_V2 is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_TMPFS=y +CONFIG_RAMFS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Console drivers +# +CONFIG_VGA_CONSOLE=y +# CONFIG_VIDEO_SELECT is not set +# CONFIG_MDA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Sound +# +CONFIG_SOUND=y +# CONFIG_SOUND_ALI5455 is not set +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_CMPCI is not set +CONFIG_SOUND_EMU10K1=y +CONFIG_MIDI_EMU10K1=y +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_FORTE is not set +# CONFIG_SOUND_ICH is not set +# CONFIG_SOUND_RME96XX is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_VIA82CXXX is not set +# CONFIG_MIDI_VIA82CXXX is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set +# CONFIG_SOUND_AD1980 is not set +# CONFIG_SOUND_WM97XX is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_UHCI_ALT=y +# CONFIG_USB_OHCI is not set +# CONFIG_USB_SL811HS_ALT is not set +# CONFIG_USB_SL811HS is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDINPUT is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_BRLVGER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# Support for USB gadgets +# +# CONFIG_USB_GADGET is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_IOVIRT=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_FRAME_POINTER=y +CONFIG_LOG_BUF_SHIFT=0 + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set +# CONFIG_FW_LOADER is not set diff --git a/testsuite/configs/all-no-act b/testsuite/configs/all-no-act new file mode 100644 index 0000000..baeed43 --- /dev/null +++ b/testsuite/configs/all-no-act @@ -0,0 +1,1499 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc2-bk13 +# Wed Dec 8 14:43:07 2004 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_LOCK_KERNEL=y + +# +# General setup +# +CONFIG_LOCALVERSION="no-act" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_X86_GENERIC is not set +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_HPET_TIMER is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +# CONFIG_SCHED_SMT is not set +CONFIG_PREEMPT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +CONFIG_X86_MCE_NONFATAL=y +# CONFIG_X86_MCE_P4THERMAL is not set +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_IRQBALANCE=y +CONFIG_HAVE_DEC_LOCK=y +# CONFIG_REGPARM is not set + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_SOFTWARE_SUSPEND=y +CONFIG_PM_STD_PARTITION="" + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=y +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_ASUS is not set +# CONFIG_ACPI_IBM is not set +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +# CONFIG_X86_PM_TIMER is not set + +# +# APM (Advanced Power Management) BIOS Support +# +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set +CONFIG_PNPACPI=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y +CONFIG_BLK_DEV_NBD=y +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_LBD=y +CONFIG_CDROM_PKTCDVD=y +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_IDEPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +CONFIG_BLK_DEV_SIS5513=y +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +# CONFIG_BLK_DEV_SD is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +CONFIG_I2O=y +CONFIG_I2O_CONFIG=y +CONFIG_I2O_BLOCK=y +# CONFIG_I2O_SCSI is not set +CONFIG_I2O_PROC=y + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_IP_TCPDIAG=y +CONFIG_IP_TCPDIAG_IPV6=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_INET6_TUNNEL=y +CONFIG_IPV6_TUNNEL=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CONNTRACK_MARK=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +CONFIG_IP_NF_FTP=y +CONFIG_IP_NF_IRC=y +CONFIG_IP_NF_TFTP=y +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_LIMIT=y +CONFIG_IP_NF_MATCH_IPRANGE=y +CONFIG_IP_NF_MATCH_MAC=y +CONFIG_IP_NF_MATCH_PKTTYPE=y +CONFIG_IP_NF_MATCH_MARK=y +CONFIG_IP_NF_MATCH_MULTIPORT=y +CONFIG_IP_NF_MATCH_TOS=y +CONFIG_IP_NF_MATCH_RECENT=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_DSCP=y +CONFIG_IP_NF_MATCH_AH_ESP=y +CONFIG_IP_NF_MATCH_LENGTH=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_MATCH_TCPMSS=y +CONFIG_IP_NF_MATCH_HELPER=y +CONFIG_IP_NF_MATCH_STATE=y +CONFIG_IP_NF_MATCH_CONNTRACK=y +CONFIG_IP_NF_MATCH_OWNER=y +CONFIG_IP_NF_MATCH_PHYSDEV=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_REALM=y +# CONFIG_IP_NF_MATCH_SCTP is not set +CONFIG_IP_NF_MATCH_COMMENT=y +CONFIG_IP_NF_MATCH_CONNMARK=y +CONFIG_IP_NF_MATCH_HASHLIMIT=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_IP_NF_TARGET_TCPMSS=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_SAME=y +# CONFIG_IP_NF_NAT_LOCAL is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_IRC=y +CONFIG_IP_NF_NAT_FTP=y +CONFIG_IP_NF_NAT_TFTP=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_TOS=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_DSCP=y +CONFIG_IP_NF_TARGET_MARK=y +CONFIG_IP_NF_TARGET_CLASSIFY=y +CONFIG_IP_NF_TARGET_CONNMARK=y +CONFIG_IP_NF_TARGET_CLUSTERIP=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_TARGET_NOTRACK=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_IP6_NF_QUEUE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_LIMIT=y +CONFIG_IP6_NF_MATCH_MAC=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_MULTIPORT=y +CONFIG_IP6_NF_MATCH_OWNER=y +CONFIG_IP6_NF_MATCH_MARK=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_AHESP=y +CONFIG_IP6_NF_MATCH_LENGTH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_PHYSDEV=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_TARGET_MARK=y +CONFIG_IP6_NF_RAW=y + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +# CONFIG_ATM_CLIP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +CONFIG_LLC2=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_EGP=y +CONFIG_EGP_DEBUG=y +CONFIG_NET_CLS_EGP_SIMPLE_CMP=y +CONFIG_NET_CLS_EGP_NBYTE=y +CONFIG_NET_CLS_EGP_KMP=y +CONFIG_NET_CLS_EGP_REGEXP=y +CONFIG_NET_CLS_EGP_CMD=y +CONFIG_EGP_CMD_BACK_TTL=4096 +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=y +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_NET_SB1000 is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_CS89x0 is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPPOE is not set +# CONFIG_PPPOATM is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +CONFIG_SHAPER=y +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +CONFIG_I2C_SIS96X=y +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +# CONFIG_IBM_ASM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set +# CONFIG_VIDEO_SELECT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ISA devices +# +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SSCAPE is not set + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS4281 is not set +CONFIG_SND_EMU10K1=y +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VX222 is not set + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +CONFIG_USB_EGALAX=m +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGETKIT is not set +CONFIG_USB_PHIDGETSERVO=m +# CONFIG_USB_TEST is not set + +# +# USB ATM/DSL drivers +# +# CONFIG_USB_ATM is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHEDSTATS is not set +CONFIG_DEBUG_SLAB=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +CONFIG_EARLY_PRINTK=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_KPROBES is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_4KSTACKS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=m +CONFIG_SECURITY_ROOTPLUG=m +# CONFIG_SECURITY_SECLVL is not set +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_SERPENT=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_CAST5=y +CONFIG_CRYPTO_CAST6=y +CONFIG_CRYPTO_TEA=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_KHAZAD=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_TEST=y + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/testsuite/configs/all-police-act b/testsuite/configs/all-police-act new file mode 100644 index 0000000..1c84282 --- /dev/null +++ b/testsuite/configs/all-police-act @@ -0,0 +1,1504 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-rc2-bk13 +# Wed Dec 8 14:19:17 2004 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_LOCK_KERNEL=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +CONFIG_MPENTIUM4=y +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_X86_GENERIC is not set +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_HPET_TIMER is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +# CONFIG_SCHED_SMT is not set +CONFIG_PREEMPT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +CONFIG_X86_MCE_NONFATAL=y +# CONFIG_X86_MCE_P4THERMAL is not set +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_NOHIGHMEM=y +# CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM64G is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_IRQBALANCE=y +CONFIG_HAVE_DEC_LOCK=y +# CONFIG_REGPARM is not set + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_SOFTWARE_SUSPEND=y +CONFIG_PM_STD_PARTITION="" + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_VIDEO=y +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_ASUS is not set +# CONFIG_ACPI_IBM is not set +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +# CONFIG_X86_PM_TIMER is not set + +# +# APM (Advanced Power Management) BIOS Support +# +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=m +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set +CONFIG_PNPACPI=y + +# +# Block devices +# +CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y +CONFIG_BLK_DEV_NBD=y +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_LBD=y +CONFIG_CDROM_PKTCDVD=y +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_CMD640=y +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_IDEPNP is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +CONFIG_BLK_DEV_SIS5513=y +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +# CONFIG_BLK_DEV_SD is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +CONFIG_I2O=y +CONFIG_I2O_CONFIG=y +CONFIG_I2O_BLOCK=y +# CONFIG_I2O_SCSI is not set +CONFIG_I2O_PROC=y + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y +CONFIG_IP_TCPDIAG=y +CONFIG_IP_TCPDIAG_IPV6=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_INET6_TUNNEL=y +CONFIG_IPV6_TUNNEL=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CONNTRACK_MARK=y +# CONFIG_IP_NF_CT_PROTO_SCTP is not set +CONFIG_IP_NF_FTP=y +CONFIG_IP_NF_IRC=y +CONFIG_IP_NF_TFTP=y +# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_QUEUE=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_LIMIT=y +CONFIG_IP_NF_MATCH_IPRANGE=y +CONFIG_IP_NF_MATCH_MAC=y +CONFIG_IP_NF_MATCH_PKTTYPE=y +CONFIG_IP_NF_MATCH_MARK=y +CONFIG_IP_NF_MATCH_MULTIPORT=y +CONFIG_IP_NF_MATCH_TOS=y +CONFIG_IP_NF_MATCH_RECENT=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_DSCP=y +CONFIG_IP_NF_MATCH_AH_ESP=y +CONFIG_IP_NF_MATCH_LENGTH=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_MATCH_TCPMSS=y +CONFIG_IP_NF_MATCH_HELPER=y +CONFIG_IP_NF_MATCH_STATE=y +CONFIG_IP_NF_MATCH_CONNTRACK=y +CONFIG_IP_NF_MATCH_OWNER=y +CONFIG_IP_NF_MATCH_PHYSDEV=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_REALM=y +# CONFIG_IP_NF_MATCH_SCTP is not set +CONFIG_IP_NF_MATCH_COMMENT=y +CONFIG_IP_NF_MATCH_CONNMARK=y +CONFIG_IP_NF_MATCH_HASHLIMIT=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_IP_NF_TARGET_TCPMSS=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_SAME=y +# CONFIG_IP_NF_NAT_LOCAL is not set +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_IRC=y +CONFIG_IP_NF_NAT_FTP=y +CONFIG_IP_NF_NAT_TFTP=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_TOS=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_DSCP=y +CONFIG_IP_NF_TARGET_MARK=y +CONFIG_IP_NF_TARGET_CLASSIFY=y +CONFIG_IP_NF_TARGET_CONNMARK=y +CONFIG_IP_NF_TARGET_CLUSTERIP=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_TARGET_NOTRACK=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_IP6_NF_QUEUE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_LIMIT=y +CONFIG_IP6_NF_MATCH_MAC=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_MULTIPORT=y +CONFIG_IP6_NF_MATCH_OWNER=y +CONFIG_IP6_NF_MATCH_MARK=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_AHESP=y +CONFIG_IP6_NF_MATCH_LENGTH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_PHYSDEV=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_TARGET_MARK=y +CONFIG_IP6_NF_RAW=y + +# +# Bridge: Netfilter Configuration +# +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +CONFIG_ATM=y +# CONFIG_ATM_CLIP is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_BRIDGE=y +CONFIG_VLAN_8021Q=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +CONFIG_LLC2=y +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_ATM=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_CLS_EGP=y +CONFIG_EGP_DEBUG=y +CONFIG_NET_CLS_EGP_SIMPLE_CMP=y +CONFIG_NET_CLS_EGP_NBYTE=y +CONFIG_NET_CLS_EGP_KMP=y +CONFIG_NET_CLS_EGP_REGEXP=y +CONFIG_NET_CLS_EGP_CMD=y +CONFIG_EGP_CMD_BACK_TTL=4096 +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=y +CONFIG_NET_CLS_RSVP6=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_IPT=y +CONFIG_NET_ACT_PEDIT=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=y +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_NET_SB1000 is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_CS89x0 is not set +# CONFIG_DGRS is not set +# CONFIG_EEPRO100 is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +CONFIG_8139TOO=y +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +# CONFIG_8139TOO_8129 is not set +# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPPOE is not set +# CONFIG_PPPOATM is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +CONFIG_SHAPER=y +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +CONFIG_I2C_SIS96X=y +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=y +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=y +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +# CONFIG_IBM_ASM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set +# CONFIG_VIDEO_SELECT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_SEQUENCER=y +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ISA devices +# +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SSCAPE is not set + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=y +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS4281 is not set +CONFIG_SND_EMU10K1=y +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VX222 is not set + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +CONFIG_USB_EGALAX=m +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGETKIT is not set +CONFIG_USB_PHIDGETSERVO=m +# CONFIG_USB_TEST is not set + +# +# USB ATM/DSL drivers +# +# CONFIG_USB_ATM is not set +# CONFIG_USB_SPEEDTOUCH is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHEDSTATS is not set +CONFIG_DEBUG_SLAB=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +CONFIG_EARLY_PRINTK=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_KPROBES is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_4KSTACKS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=m +CONFIG_SECURITY_ROOTPLUG=m +# CONFIG_SECURITY_SECLVL is not set +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_SERPENT=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_CAST5=y +CONFIG_CRYPTO_CAST6=y +CONFIG_CRYPTO_TEA=y +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_KHAZAD=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_TEST=y + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/testsuite/tests/policer b/testsuite/tests/policer new file mode 100644 index 0000000..eaf16ac --- /dev/null +++ b/testsuite/tests/policer @@ -0,0 +1,13 @@ +#!/bin/sh +$TC qdisc del dev $DEV root >/dev/null 2>&1 +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC filter add dev $DEV parent 10:0 protocol ip prio 10 u32 match ip protocol 1 0xff police rate 2kbit buffer 10k drop flowid 10:12 +$TC qdisc list dev $DEV +$TC filter list dev $DEV parent 10:0 +$TC qdisc del dev $DEV root +$TC qdisc list dev $DEV +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC filter add dev $DEV parent 10:0 protocol ip prio 10 u32 match ip protocol 1 0xff police rate 2kbit buffer 10k drop flowid 10:12 +$TC qdisc del dev $DEV root diff --git a/testsuite/tests/std-cbq b/testsuite/tests/std-cbq new file mode 100644 index 0000000..bff814b --- /dev/null +++ b/testsuite/tests/std-cbq @@ -0,0 +1,10 @@ +#!/bin/sh +$TC qdisc del dev $DEV root >/dev/null 2>&1 +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC qdisc list dev $DEV +$TC qdisc del dev $DEV root +$TC qdisc list dev $DEV +$TC qdisc add dev $DEV root handle 10:0 cbq bandwidth 100Mbit avpkt 1400 mpu 64 +$TC class add dev $DEV parent 10:0 classid 10:12 cbq bandwidth 100mbit rate 100mbit allot 1514 prio 3 maxburst 1 avpkt 500 bounded +$TC qdisc del dev $DEV root -- 2.43.0